[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