From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: From: Vladislav Shpilevoy Subject: [PATCH v2 02/10] yaml: introduce yaml.encode_tagged Date: Fri, 20 Apr 2018 16:24:27 +0300 Message-Id: <7c30b9483a38bd36425d7e0c87b4d15e8893446e.1524228894.git.v.shpilevoy@tarantool.org> In-Reply-To: References: In-Reply-To: References: To: tarantool-patches@freelists.org Cc: vdavydov.dev@gmail.com List-ID: 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 = , handle = }, 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 = , "\ + "handle = }, 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)