From: Stephan Bosch Date: Mon, 31 Jul 2023 10:50:37 +0000 (+0200) Subject: lib-json: json-tree - Implement tree walker X-Git-Tag: 2.4.0~2382 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4b56f7a35b79b630b690fdae5ac9026c81d81ba2;p=thirdparty%2Fdovecot%2Fcore.git lib-json: json-tree - Implement tree walker --- diff --git a/src/lib-json/Makefile.am b/src/lib-json/Makefile.am index 2ce0ec99af..bcc815ae23 100644 --- a/src/lib-json/Makefile.am +++ b/src/lib-json/Makefile.am @@ -28,7 +28,8 @@ test_programs = \ test-json-generator \ test-json-io \ test-json-istream \ - test-json-ostream + test-json-ostream \ + test-json-tree noinst_PROGRAMS = $(test_programs) @@ -79,6 +80,13 @@ test_json_ostream_LDADD = \ test_json_ostream_DEPENDENCIES = \ $(test_deps) +test_json_tree_SOURCE = \ + test-json-tree.c +test_json_tree_LDADD = \ + $(test_libs) +test_json_tree_DEPENDENCIES = \ + $(test_deps) + pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) diff --git a/src/lib-json/json-tree.new.c b/src/lib-json/json-tree.new.c index c3e33d1976..2728232a54 100644 --- a/src/lib-json/json-tree.new.c +++ b/src/lib-json/json-tree.new.c @@ -605,3 +605,145 @@ json_tree_node_get_child_with(const struct json_tree_node *jtnode, return child; } + +/* + * Walker + */ + +struct json_tree_walker { + const struct json_tree_node *root, *node; + ARRAY_TYPE(json_tree_node_const) sub_nodes; + unsigned int node_level; + + bool node_is_end:1; +}; + +struct json_tree_walker * +json_tree_walker_create_from_node(const struct json_tree_node *tree_node) +{ + struct json_tree_walker *twalker; + + i_assert(tree_node != NULL); + + twalker = i_new(struct json_tree_walker, 1); + twalker->root = tree_node; + + return twalker; +} + +struct json_tree_walker * +json_tree_walker_create(const struct json_tree *tree) +{ + i_assert(tree != NULL); + return json_tree_walker_create_from_node( + json_tree_get_root_const(tree)); +} + +void json_tree_walker_free(struct json_tree_walker **_twalker) +{ + struct json_tree_walker *twalker = *_twalker; + + if (twalker == NULL) + return; + *_twalker = NULL; + + array_free(&twalker->sub_nodes); + i_free(twalker); +} + +static const struct json_tree_node * +json_tree_walk_next(struct json_tree_walker *twalker, bool *is_end_r) +{ + const struct json_tree_node *tnode = twalker->node, *tnode_next; + + *is_end_r = FALSE; + + if (tnode == NULL) { + i_assert(twalker->node_level == 0); + twalker->node_level++; + return twalker->root; + } + + bool tnode_is_end = twalker->node_is_end; + const struct json_node *node = &tnode->node; + + if (!json_node_is_singular(node) && !tnode_is_end) { + tnode_next = json_tree_node_get_child(tnode); + if (tnode_next != NULL) { + twalker->node_level++; + return tnode_next; + } + *is_end_r = TRUE; + return tnode; + } + + tnode_next = json_tree_node_get_next(tnode); + if (tnode_next != NULL || twalker->node_level == 0) + return tnode_next; + + twalker->node_level--; + *is_end_r = TRUE; + return json_tree_node_get_parent(tnode); +} + +bool json_tree_walk(struct json_tree_walker *twalker, struct json_node *node_r) +{ + const struct json_tree_node *tnode_next; + bool tnode_next_is_end; + + tnode_next = json_tree_walk_next(twalker, &tnode_next_is_end); + if (tnode_next == NULL) { + i_assert(twalker->node_level == 0); + i_zero(node_r); + twalker->node = twalker->root = NULL; + twalker->node_is_end = TRUE; + return FALSE; + } + if (json_tree_node_is_root(tnode_next) && twalker->node_level > 1) { + const struct json_tree_node *tnode_sub = tnode_next; + + /* Returned to root of subtree */ + i_assert(tnode_next_is_end); + i_assert(array_is_created(&twalker->sub_nodes)); + i_assert(array_count(&twalker->sub_nodes) > 0); + tnode_next = *array_back(&twalker->sub_nodes); + array_pop_back(&twalker->sub_nodes); + + i_zero(node_r); + node_r->name = tnode_next->node.name; + node_r->type = tnode_sub->node.type; + + twalker->node = tnode_next; + twalker->node_is_end = TRUE; + return TRUE; + } + + const struct json_node *node_next = &tnode_next->node; + + if (tnode_next_is_end) { + i_zero(node_r); + node_r->name = node_next->name; + node_r->type = node_next->type; + } else if (node_next->type == JSON_TYPE_TEXT && + node_next->value.content_type == JSON_CONTENT_TYPE_TREE) { + struct json_tree *tree = node_next->value.content.tree; + const struct json_tree_node *tnode_sub; + + /* Descend into subtree */ + if (!array_is_created(&twalker->sub_nodes)) + i_array_init(&twalker->sub_nodes, 4); + array_push_back(&twalker->sub_nodes, &tnode_next); + tnode_sub = json_tree_get_root(tree); + i_assert(tnode_sub != NULL); + i_assert(tnode_sub->node.type != JSON_TYPE_NONE); + *node_r = tnode_sub->node; + node_r->name = node_next->name; + tnode_next = tnode_sub; + } else { + *node_r = tnode_next->node; + } + + twalker->node = tnode_next; + twalker->node_is_end = tnode_next_is_end; + return TRUE; +} diff --git a/src/lib-json/json-tree.new.h b/src/lib-json/json-tree.new.h index 873319b12f..0a6eb371f2 100644 --- a/src/lib-json/json-tree.new.h +++ b/src/lib-json/json-tree.new.h @@ -254,4 +254,18 @@ bool json_tree_is_false(const struct json_tree *jtree) ATTR_PURE; bool json_tree_is_boolean(const struct json_tree *jtree) ATTR_PURE; bool json_tree_is_null(const struct json_tree *jtree) ATTR_PURE; +/* + * Walker + */ + +struct json_tree_walker; + +struct json_tree_walker * +json_tree_walker_create_from_node(const struct json_tree_node *tree_node); +struct json_tree_walker * +json_tree_walker_create(const struct json_tree *tree); +void json_tree_walker_free(struct json_tree_walker **_twalker); + +bool json_tree_walk(struct json_tree_walker *twalker, struct json_node *node_r); + #endif diff --git a/src/lib-json/test-json-tree.c b/src/lib-json/test-json-tree.c new file mode 100644 index 0000000000..63c6ccd358 --- /dev/null +++ b/src/lib-json/test-json-tree.c @@ -0,0 +1,518 @@ +/* Copyright (c) 2017-2023 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "str.h" +#include "istream.h" +#include "ostream.h" +#include "test-common.h" + +#include "json-tree.new.h" + +#include + +static bool debug = FALSE; + +static void test_stream_value(struct istream *val_input, const char *expected) +{ + const unsigned char *data; + size_t size; + string_t *buffer; + int ret; + + buffer = str_new(default_pool, 256); + + while ((ret = i_stream_read_more(val_input, &data, &size)) > 0) { + str_append_data(buffer, data, size); + i_stream_skip(val_input, size); + } + if (ret < 0) + test_assert(!i_stream_have_bytes_left(val_input)); + + test_assert_strcmp(str_c(buffer), expected); + str_free(&buffer); +} + +static void test_json_tree_walker(void) +{ + struct istream *input; + const char *data; + struct json_tree *jtree, *jtree2, *jtree3; + struct json_tree_node *jtnode; + struct json_tree_walker *jtwalker; + struct json_node jnode; + intmax_t num_val = 0; + + /* number */ + test_begin("json tree walker - number"); + jtree = json_tree_create(); + (void)json_tree_node_add_number_int( + json_tree_get_root(jtree), NULL, 23423); + jtwalker = json_tree_walker_create(jtree); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_number(&jnode)); + test_assert(json_node_get_intmax(&jnode, &num_val) == 0); + test_assert(num_val == 23423); + test_assert(!json_tree_walk(jtwalker, &jnode)); + json_tree_walker_free(&jtwalker); + json_tree_unref(&jtree); + test_end(); + + /* false */ + test_begin("json tree walker - false"); + jtree = json_tree_create(); + (void)json_tree_node_add_false( + json_tree_get_root(jtree), NULL); + jtwalker = json_tree_walker_create(jtree); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_false(&jnode)); + test_assert(!json_tree_walk(jtwalker, &jnode)); + json_tree_walker_free(&jtwalker); + json_tree_unref(&jtree); + test_end(); + + /* null */ + test_begin("json tree walker - null"); + jtree = json_tree_create(); + (void)json_tree_node_add_null( + json_tree_get_root(jtree), NULL); + jtwalker = json_tree_walker_create(jtree); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_null(&jnode)); + test_assert(!json_tree_walk(jtwalker, &jnode)); + json_tree_walker_free(&jtwalker); + json_tree_unref(&jtree); + test_end(); + + /* true */ + test_begin("json tree walker - true"); + jtree = json_tree_create(); + (void)json_tree_node_add_true( + json_tree_get_root(jtree), NULL); + jtwalker = json_tree_walker_create(jtree); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_true(&jnode)); + test_assert(!json_tree_walk(jtwalker, &jnode)); + json_tree_walker_free(&jtwalker); + json_tree_unref(&jtree); + test_end(); + + /* string */ + test_begin("json tree walker - string"); + jtree = json_tree_create(); + (void)json_tree_node_add_string( + json_tree_get_root(jtree), NULL, "frop"); + jtwalker = json_tree_walker_create(jtree); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_string(&jnode)); + test_assert_strcmp(json_node_get_str(&jnode), "frop"); + test_assert(!json_tree_walk(jtwalker, &jnode)); + json_tree_walker_free(&jtwalker); + json_tree_unref(&jtree); + test_end(); + + /* string stream */ + test_begin("json tree walker - string stream"); + jtree = json_tree_create(); + data = "AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ"; + input = i_stream_create_from_data(data, strlen(data)); + (void)json_tree_node_add_string_stream( + json_tree_get_root(jtree), NULL, input); + i_stream_unref(&input); + jtwalker = json_tree_walker_create(jtree); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_string(&jnode)); + test_assert(jnode.value.content_type == JSON_CONTENT_TYPE_STREAM); + test_assert(jnode.value.content.stream != NULL); + test_stream_value(jnode.value.content.stream, data); + test_assert(!json_tree_walk(jtwalker, &jnode)); + json_tree_walker_free(&jtwalker); + json_tree_unref(&jtree); + test_end(); + + /* array */ + test_begin("json tree walker - array"); + jtree = json_tree_create(); + (void)json_tree_node_add_array( + json_tree_get_root(jtree), NULL); + jtwalker = json_tree_walker_create(jtree); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_array(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_array_end(&jnode)); + test_assert(!json_tree_walk(jtwalker, &jnode)); + json_tree_walker_free(&jtwalker); + json_tree_unref(&jtree); + test_end(); + + /* [ string ] */ + test_begin("json tree walker - array [ string ]"); + jtree = json_tree_create(); + jtnode = json_tree_node_add_array( + json_tree_get_root(jtree), NULL); + (void)json_tree_node_add_string(jtnode, NULL, "frop"); + jtwalker = json_tree_walker_create(jtree); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_array(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_string(&jnode)); + test_assert_strcmp(json_node_get_str(&jnode), "frop"); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_array_end(&jnode)); + test_assert(!json_tree_walk(jtwalker, &jnode)); + json_tree_walker_free(&jtwalker); + json_tree_unref(&jtree); + test_end(); + + /* [ string stream ] */ + test_begin("json tree walker - array [ string stream ]"); + jtree = json_tree_create(); + jtnode = json_tree_node_add_array( + json_tree_get_root(jtree), NULL); + data = "AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ"; + input = i_stream_create_from_data(data, strlen(data)); + (void)json_tree_node_add_string_stream(jtnode, NULL, input); + i_stream_unref(&input); + jtwalker = json_tree_walker_create(jtree); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_array(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_string(&jnode)); + test_assert(jnode.value.content_type == JSON_CONTENT_TYPE_STREAM); + test_assert(jnode.value.content.stream != NULL); + test_stream_value(jnode.value.content.stream, data); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_array_end(&jnode)); + test_assert(!json_tree_walk(jtwalker, &jnode)); + json_tree_walker_free(&jtwalker); + json_tree_unref(&jtree); + test_end(); + + /* object */ + test_begin("json tree walker - object"); + jtree = json_tree_create(); + (void)json_tree_node_add_object( + json_tree_get_root(jtree), NULL); + jtwalker = json_tree_walker_create(jtree); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_object(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_object_end(&jnode)); + test_assert(!json_tree_walk(jtwalker, &jnode)); + json_tree_unref(&jtree); + json_tree_walker_free(&jtwalker); + test_end(); + + /* { member: string } */ + test_begin("json tree walker - object { member: string }"); + jtree = json_tree_create(); + jtnode = json_tree_node_add_object( + json_tree_get_root(jtree), NULL); + (void)json_tree_node_add_string(jtnode, "frop", "friep"); + jtwalker = json_tree_walker_create(jtree); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_object(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_string(&jnode)); + test_assert_strcmp(jnode.name, "frop"); + test_assert_strcmp(json_node_get_str(&jnode), "friep"); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_object_end(&jnode)); + test_assert(!json_tree_walk(jtwalker, &jnode)); + json_tree_unref(&jtree); + json_tree_walker_free(&jtwalker); + test_end(); + + /* { member: string stream } */ + test_begin("json tree walker - object { member: string stream }"); + jtree = json_tree_create(); + jtnode = json_tree_node_add_object( + json_tree_get_root(jtree), NULL); + data = "AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ"; + input = i_stream_create_from_data(data, strlen(data)); + (void)json_tree_node_add_string_stream(jtnode, "frop", input); + i_stream_unref(&input); + jtwalker = json_tree_walker_create(jtree); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_object(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_string(&jnode)); + test_assert_strcmp(jnode.name, "frop"); + test_assert(jnode.value.content_type == JSON_CONTENT_TYPE_STREAM); + test_assert(jnode.value.content.stream != NULL); + test_stream_value(jnode.value.content.stream, data); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_object_end(&jnode)); + test_assert(!json_tree_walk(jtwalker, &jnode)); + json_tree_walker_free(&jtwalker); + json_tree_unref(&jtree); + test_end(); + + /* { "a": [{"d": 1}], "b": [{"e": 2}], "c": [{"f": 3}] } */ + test_begin("json tree walker - object { \"a\": [{\"d\": 1}], \"b\": [{\"e\": 2}], \"c\": [{\"f\": 3}] }"); + jtree = json_tree_create(); + jtnode = json_tree_node_add_object( + json_tree_get_root(jtree), NULL); + jtnode = json_tree_node_add_array(jtnode, "a"); + jtnode = json_tree_node_add_object(jtnode, NULL); + (void)json_tree_node_add_number_int(jtnode, "d", 1); + jtnode = json_tree_node_get_parent(jtnode); + jtnode = json_tree_node_get_parent(jtnode); + jtnode = json_tree_node_add_array(jtnode, "b"); + jtnode = json_tree_node_add_object(jtnode, NULL); + (void)json_tree_node_add_number_int(jtnode, "e", 2); + jtnode = json_tree_node_get_parent(jtnode); + jtnode = json_tree_node_get_parent(jtnode); + jtnode = json_tree_node_add_array(jtnode, "c"); + jtnode = json_tree_node_add_object(jtnode, NULL); + (void)json_tree_node_add_number_int(jtnode, "f", 3); + jtwalker = json_tree_walker_create(jtree); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_object(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_array(&jnode)); + test_assert_strcmp(jnode.name, "a"); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_object(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_number(&jnode)); + test_assert_strcmp(jnode.name, "d"); + test_assert(json_node_get_intmax(&jnode, &num_val) == 0); + test_assert(num_val == 1); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_object_end(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_array_end(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_array(&jnode)); + test_assert_strcmp(jnode.name, "b"); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_object(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_number(&jnode)); + test_assert_strcmp(jnode.name, "e"); + test_assert(json_node_get_intmax(&jnode, &num_val) == 0); + test_assert(num_val == 2); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_object_end(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_array_end(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_array(&jnode)); + test_assert_strcmp(jnode.name, "c"); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_object(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_number(&jnode)); + test_assert_strcmp(jnode.name, "f"); + test_assert(json_node_get_intmax(&jnode, &num_val) == 0); + test_assert(num_val == 3); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_object_end(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_array_end(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_object_end(&jnode)); + test_assert(!json_tree_walk(jtwalker, &jnode)); + json_tree_unref(&jtree); + json_tree_walker_free(&jtwalker); + test_end(); + + /* { "a": [{"d": 1}], "b": [{"e": 2}], "c": [{"f": 3}] } */ + test_begin("json tree walker - nested trees"); + jtree = json_tree_create(); + jtnode = json_tree_get_root(jtree); + jtnode = json_tree_node_add_object(jtnode, NULL); + jtree2 = json_tree_create(); + jtnode = json_tree_get_root(jtree2); + jtnode = json_tree_node_add_array(jtnode, NULL); + jtnode = json_tree_node_add_object(jtnode, NULL); + (void)json_tree_node_add_number_int(jtnode, "d", 1); + jtnode = json_tree_get_root(jtree); + json_tree_node_add_subtree(jtnode, "a", jtree2); + json_tree_unref(&jtree2); + jtree2 = json_tree_create(); + jtnode = json_tree_get_root(jtree2); + jtnode = json_tree_node_add_array(jtnode, NULL); + jtnode = json_tree_node_add_object(jtnode, NULL); + (void)json_tree_node_add_number_int(jtnode, "e", 2); + jtnode = json_tree_get_root(jtree); + json_tree_node_add_subtree(jtnode, "b", jtree2); + json_tree_unref(&jtree2); + jtree2 = json_tree_create(); + jtnode = json_tree_get_root(jtree2); + jtnode = json_tree_node_add_array(jtnode, NULL); + jtnode = json_tree_node_add_object(jtnode, NULL); + (void)json_tree_node_add_number_int(jtnode, "f", 3); + jtnode = json_tree_get_root(jtree); + json_tree_node_add_subtree(jtnode, "c", jtree2); + json_tree_unref(&jtree2); + jtwalker = json_tree_walker_create(jtree); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_object(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_array(&jnode)); + test_assert_strcmp(jnode.name, "a"); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_object(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_number(&jnode)); + test_assert_strcmp(jnode.name, "d"); + test_assert(json_node_get_intmax(&jnode, &num_val) == 0); + test_assert(num_val == 1); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_object_end(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_array_end(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_array(&jnode)); + test_assert_strcmp(jnode.name, "b"); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_object(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_number(&jnode)); + test_assert_strcmp(jnode.name, "e"); + test_assert(json_node_get_intmax(&jnode, &num_val) == 0); + test_assert(num_val == 2); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_object_end(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_array_end(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_array(&jnode)); + test_assert_strcmp(jnode.name, "c"); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_object(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_number(&jnode)); + test_assert_strcmp(jnode.name, "f"); + test_assert(json_node_get_intmax(&jnode, &num_val) == 0); + test_assert(num_val == 3); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_object_end(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_array_end(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_object_end(&jnode)); + test_assert(!json_tree_walk(jtwalker, &jnode)); + json_tree_walker_free(&jtwalker); + json_tree_unref(&jtree); + test_end(); + + /* { "a": [{"d": 1}], "b": [{"e": 2}], "c": [{"f": 3}] } */ + test_begin("json tree walker - doubly nested trees"); + jtree = json_tree_create(); + jtnode = json_tree_get_root(jtree); + jtnode = json_tree_node_add_object(jtnode, NULL); + jtree2 = json_tree_create(); + jtnode = json_tree_get_root(jtree2); + jtnode = json_tree_node_add_array(jtnode, NULL); + jtree3 = json_tree_create(); + jtnode = json_tree_get_root(jtree3); + jtnode = json_tree_node_add_object(jtnode, NULL); + (void)json_tree_node_add_number_int(jtnode, "d", 1); + jtnode = json_tree_get_root(jtree2); + json_tree_node_add_subtree(jtnode, NULL, jtree3); + json_tree_unref(&jtree3); + jtnode = json_tree_get_root(jtree); + json_tree_node_add_subtree(jtnode, "a", jtree2); + json_tree_unref(&jtree2); + jtree2 = json_tree_create(); + jtnode = json_tree_get_root(jtree2); + jtnode = json_tree_node_add_array(jtnode, NULL); + jtree3 = json_tree_create(); + jtnode = json_tree_get_root(jtree3); + jtnode = json_tree_node_add_object(jtnode, NULL); + (void)json_tree_node_add_number_int(jtnode, "e", 2); + jtnode = json_tree_get_root(jtree2); + json_tree_node_add_subtree(jtnode, NULL, jtree3); + json_tree_unref(&jtree3); + jtnode = json_tree_get_root(jtree); + json_tree_node_add_subtree(jtnode, "b", jtree2); + json_tree_unref(&jtree2); + jtree2 = json_tree_create(); + jtnode = json_tree_get_root(jtree2); + jtnode = json_tree_node_add_array(jtnode, NULL); + jtree3 = json_tree_create(); + jtnode = json_tree_get_root(jtree3); + jtnode = json_tree_node_add_object(jtnode, NULL); + (void)json_tree_node_add_number_int(jtnode, "f", 3); + jtnode = json_tree_get_root(jtree2); + json_tree_node_add_subtree(jtnode, NULL, jtree3); + json_tree_unref(&jtree3); + jtnode = json_tree_get_root(jtree); + json_tree_node_add_subtree(jtnode, "c", jtree2); + json_tree_unref(&jtree2); + jtwalker = json_tree_walker_create(jtree); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_object(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_array(&jnode)); + test_assert_strcmp(jnode.name, "a"); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_object(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_number(&jnode)); + test_assert_strcmp(jnode.name, "d"); + test_assert(json_node_get_intmax(&jnode, &num_val) == 0); + test_assert(num_val == 1); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_object_end(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_array_end(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_array(&jnode)); + test_assert_strcmp(jnode.name, "b"); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_object(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_number(&jnode)); + test_assert_strcmp(jnode.name, "e"); + test_assert(json_node_get_intmax(&jnode, &num_val) == 0); + test_assert(num_val == 2); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_object_end(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_array_end(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_array(&jnode)); + test_assert_strcmp(jnode.name, "c"); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_object(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_number(&jnode)); + test_assert_strcmp(jnode.name, "f"); + test_assert(json_node_get_intmax(&jnode, &num_val) == 0); + test_assert(num_val == 3); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_object_end(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_array_end(&jnode)); + test_assert(json_tree_walk(jtwalker, &jnode)); + test_assert(json_node_is_object_end(&jnode)); + test_assert(!json_tree_walk(jtwalker, &jnode)); + json_tree_walker_free(&jtwalker); + json_tree_unref(&jtree); + test_end(); +} + +int main(int argc, char *argv[]) +{ + int c; + + static void (*test_functions[])(void) = { + test_json_tree_walker, + NULL + }; + + while ((c = getopt(argc, argv, "D")) > 0) { + switch (c) { + case 'D': + debug = TRUE; + break; + default: + i_fatal("Usage: %s [-D]", argv[0]); + } + } + + return test_run(test_functions); +}