[PATCH v2 02/10] yaml: introduce yaml.encode_tagged
Vladislav Shpilevoy
v.shpilevoy at tarantool.org
Fri Apr 20 16:24:27 MSK 2018
Encode_tagged allows to define one global YAML tag for a
document. Tagged YAML documents are going to be used for
console text pushes to distinguish actual box.session.push() from
console.print(). The first will have tag !push, and the
second - !print.
---
test/app-tap/yaml.test.lua | 23 ++++++++++++++-
third_party/lua-yaml/lyaml.cc | 69 +++++++++++++++++++++++++++++++++++++++++--
2 files changed, 88 insertions(+), 4 deletions(-)
diff --git a/test/app-tap/yaml.test.lua b/test/app-tap/yaml.test.lua
index def278570..b81402cc0 100755
--- a/test/app-tap/yaml.test.lua
+++ b/test/app-tap/yaml.test.lua
@@ -69,9 +69,29 @@ local function test_output(test, s)
"--- |-\n Tutorial -- Header\n ====\n\n Text\n...\n", "tutorial string");
end
+local function test_tagged(test, s)
+ test:plan(7)
+ local must_be_err = 'Usage: encode_tagged({prefix = <string>, handle = <string>}, object)'
+ local prefix = 'tag:tarantool.io/push,2018'
+ local ok, err = pcall(s.encode_tagged)
+ test:is(err, must_be_err, "encode_tagged usage")
+ ok, err = pcall(s.encode_tagged, {}, 100)
+ test:is(err, must_be_err, "encode_tagged usage")
+ ok, err = pcall(s.encode_tagged, {handle = true, prefix = 100}, 200)
+ test:is(err, must_be_err, "encode_tagged usage")
+ local ret
+ ret, err = s.encode_tagged({handle = '!push', prefix = prefix}, 300)
+ test:is(ret, nil, 'non-usage and non-oom errors do not raise')
+ test:is(err, "tag handle must end with '!'", "encode_tagged usage")
+ ret = s.encode_tagged({handle = '!push!', prefix = prefix}, 300)
+ test:is(ret, "%TAG !push! "..prefix.."\n--- 300\n...\n", "encode_tagged usage")
+ ret = s.encode_tagged({handle = '!print!', prefix = prefix}, {a = 100, b = 200})
+ test:is(ret, "%TAG !print! tag:tarantool.io/push,2018\n---\na: 100\nb: 200\n...\n", 'encode_tagged usage')
+end
+
tap.test("yaml", function(test)
local serializer = require('yaml')
- test:plan(10)
+ test:plan(11)
test:test("unsigned", common.test_unsigned, serializer)
test:test("signed", common.test_signed, serializer)
test:test("double", common.test_double, serializer)
@@ -82,4 +102,5 @@ tap.test("yaml", function(test)
test:test("ucdata", common.test_ucdata, serializer)
test:test("compact", test_compact, serializer)
test:test("output", test_output, serializer)
+ test:test("tagged", test_tagged, serializer)
end)
diff --git a/third_party/lua-yaml/lyaml.cc b/third_party/lua-yaml/lyaml.cc
index 430f4be2c..8329f84e9 100644
--- a/third_party/lua-yaml/lyaml.cc
+++ b/third_party/lua-yaml/lyaml.cc
@@ -78,6 +78,8 @@ struct lua_yaml_dumper {
unsigned int anchor_number;
yaml_emitter_t emitter;
char error;
+ yaml_tag_directive_t begin_tag;
+ yaml_tag_directive_t *end_tag;
lua_State *outputL;
luaL_Buffer yamlbuf;
@@ -571,7 +573,8 @@ static int dump_node(struct lua_yaml_dumper *dumper)
static void dump_document(struct lua_yaml_dumper *dumper) {
yaml_event_t ev;
- if (!yaml_document_start_event_initialize(&ev, NULL, NULL, NULL, 0) ||
+ if (!yaml_document_start_event_initialize(&ev, NULL, &dumper->begin_tag,
+ dumper->end_tag, 0) ||
!yaml_emitter_emit(&dumper->emitter, &ev))
return;
@@ -618,13 +621,38 @@ static void find_references(struct lua_yaml_dumper *dumper) {
}
}
-static int l_dump(lua_State *L) {
+/**
+ * Encode a Lua object or objects into YAML documents onto Lua
+ * stack.
+ * @param L Lua stack to get arguments and push result.
+ * @param serializer Lua YAML serializer.
+ * @param tag_handle NULL, or a global tag handle. Handle becomes
+ * a synonym for prefix.
+ * @param tag_prefix NULL, or a global tag prefix, to which @a
+ * handle is expanded.
+ * @retval nil, error Error.
+ * @retval not nil Lua string with dumped object.
+ */
+static int
+lua_yaml_encode_impl(lua_State *L, struct luaL_serializer *serializer,
+ const char *tag_handle, const char *tag_prefix)
+{
struct lua_yaml_dumper dumper;
int i, argcount = lua_gettop(L);
yaml_event_t ev;
dumper.L = L;
- dumper.cfg = luaL_checkserializer(L);
+ dumper.begin_tag.handle = (yaml_char_t *) tag_handle;
+ dumper.begin_tag.prefix = (yaml_char_t *) tag_prefix;
+ /*
+ * If a tag is specified, then tag list is not empty and
+ * consists of a single tag.
+ */
+ if (tag_handle != NULL && tag_prefix != NULL)
+ dumper.end_tag = &dumper.begin_tag + 1;
+ else
+ dumper.end_tag = &dumper.begin_tag;
+ dumper.cfg = serializer;
dumper.error = 0;
/* create thread to use for YAML buffer */
dumper.outputL = lua_newthread(L);
@@ -684,11 +712,46 @@ error:
}
}
+static int l_dump(lua_State *L) {
+ return lua_yaml_encode_impl(L, luaL_checkserializer(L), NULL, NULL);
+}
+
+/**
+ * Dump a Lua object into YAML string with a global TAG specified.
+ * @param options First argument on a stack, consists of two
+ * options: tag prefix and tag handle.
+ * @param object Lua object to dump under the global tag.
+ * @retval Lua string with dumped object.
+ */
+static int l_dump_tagged(lua_State *L)
+{
+ struct luaL_serializer *serializer = luaL_checkserializer(L);
+ int argcount = lua_gettop(L);
+ if (argcount != 2 || !lua_istable(L, 1)) {
+usage_error:
+ return luaL_error(L, "Usage: encode_tagged({prefix = <string>, "\
+ "handle = <string>}, object)");
+ }
+ lua_getfield(L, 1, "prefix");
+ if (! lua_isstring(L, -1))
+ goto usage_error;
+ const char *prefix = lua_tostring(L, -1);
+ lua_getfield(L, 1, "handle");
+ if (! lua_isstring(L, -1))
+ goto usage_error;
+ const char *handle = lua_tostring(L, -1);
+ lua_pop(L, 2);
+ lua_replace(L, 1);
+ lua_settop(L, 1);
+ return lua_yaml_encode_impl(L, serializer, handle, prefix);
+}
+
static int
l_new(lua_State *L);
static const luaL_Reg yamllib[] = {
{ "encode", l_dump },
+ { "encode_tagged", l_dump_tagged },
{ "decode", l_load },
{ "new", l_new },
{ NULL, NULL}
--
2.15.1 (Apple Git-101)
More information about the Tarantool-patches
mailing list