diff --git a/common/jinja/value.cpp b/common/jinja/value.cpp index ce4c66f879..10130a6b02 100644 --- a/common/jinja/value.cpp +++ b/common/jinja/value.cpp @@ -348,6 +348,43 @@ static value default_value(const func_args & args) { return no_value ? args.get_pos(1) : args.get_pos(0); } +static value toobject(const func_args & args) { + auto out = mk_val(); + value iter = args.get_pos(0, mk_val()); + bool iter_first = false; + if (is_val(iter)) { + iter_first = true; + for (const auto & it : iter->as_array()) { + if (is_val(it) && it->as_array().size() == 2) { + auto tuple = it->as_array(); + auto key = tuple[0]; + auto val = tuple[1]; + JJ_DEBUG("namespace/dict: adding key '%s'", key->as_string().str().c_str()); + out->insert(key, val); + } else { + throw raised_exception("namespace/dict() iterable argument must consist of tuples, not " + it->type()); + } + } + } else if (is_val(iter)) { + iter_first = true; + for (const auto & pair : iter->as_ordered_object()) { + JJ_DEBUG("namespace/dict: adding key '%s'", pair.first->as_string().str().c_str()); + out->insert(pair.first, pair.second); + } + } + for (const auto & arg : args.get_args()) { + if (is_val(arg)) { + auto kwarg = cast_val(arg); + JJ_DEBUG("namespace/dict: adding key '%s'", kwarg->key.c_str()); + out->insert(kwarg->key, kwarg->val); + } else if (!iter_first) { + throw raised_exception("namespace/dict() arguments must be kwargs, dict and/or iterable of tuples, not " + arg->type()); + } + iter_first = false; + } + return out; +} + const func_builtins & global_builtins() { static const func_builtins builtins = { {"raise_exception", [](const func_args & args) -> value { @@ -355,18 +392,8 @@ const func_builtins & global_builtins() { std::string msg = args.get_pos(0)->as_string().str(); throw raised_exception("Jinja Exception: " + msg); }}, - {"namespace", [](const func_args & args) -> value { - auto out = mk_val(); - for (const auto & arg : args.get_args()) { - if (!is_val(arg)) { - throw raised_exception("namespace() arguments must be kwargs"); - } - auto kwarg = cast_val(arg); - JJ_DEBUG("namespace: adding key '%s'", kwarg->key.c_str()); - out->insert(kwarg->key, kwarg->val); - } - return out; - }}, + {"dict", toobject}, + {"namespace", toobject}, {"strftime_now", [](const func_args & args) -> value { args.ensure_vals(); std::string format = args.get_pos(0)->as_string().str(); diff --git a/tests/test-jinja.cpp b/tests/test-jinja.cpp index 50a79af3f3..891b785c4f 100644 --- a/tests/test-jinja.cpp +++ b/tests/test-jinja.cpp @@ -1976,6 +1976,30 @@ static void test_object_methods(testing & t) { json::object(), "" ); + + test_template(t, "dict from dict", + "{% set o = dict({'a': 3, 'b': 1, 'c': 2}) %}{{ o|tojson }}", + json::object(), + "{\"a\": 3, \"b\": 1, \"c\": 2}" + ); + + test_template(t, "dict from kwargs", + "{% set o = dict(a=3, b=1, c=2) %}{{ o|tojson }}", + json::object(), + "{\"a\": 3, \"b\": 1, \"c\": 2}" + ); + + test_template(t, "dict from tuples", + "{% set o = dict((obj | items | list)) %}{{ o|tojson }}", + {{"obj", {{"a", 3}, {"b", 1}, {"c", 2}}}}, + "{\"a\": 3, \"b\": 1, \"c\": 2}" + ); + + test_template(t, "dict from tuples and kwargs", + "{% set o = dict((obj | items | list), c=2) %}{{ o|tojson }}", + {{"obj", {{"a", 3}, {"b", 1}}}}, + "{\"a\": 3, \"b\": 1, \"c\": 2}" + ); } static void test_hasher(testing & t) {