data FEATURE add lyd_new_ext_path()
Support creating data defined in extension instances using (JSON) path.
diff --git a/doc/transition.dox b/doc/transition.dox
index 5606979..c1e3cf0 100644
--- a/doc/transition.dox
+++ b/doc/transition.dox
@@ -361,6 +361,7 @@
* - | ::lyd_new_opaq() | ^
* - | ::lyd_new_opaq2() | ^
* - | ::lyd_new_path2() | Supplement functionality to ::lyd_new_path().
+ * - | ::lyd_new_ext_path() | Supplement functionality to ::lyd_new_path() covering data defined in extension instances.
* lyd_insert() | ::lyd_insert_child() | Renamed to better distinguish from ::lyd_insert_sibling().
* lyd_change_leaf() | ::lyd_change_term() | Align naming with changed names of data structures.
* - | ::lyd_change_meta() | Transferred functionality of ::lyd_change_term() to metadata.
diff --git a/src/tree_data.c b/src/tree_data.c
index 0902b31..d539a5a 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -1552,13 +1552,6 @@
return ret;
}
-API LY_ERR
-lyd_new_path(struct lyd_node *parent, const struct ly_ctx *ctx, const char *path, const char *value, uint32_t options,
- struct lyd_node **node)
-{
- return lyd_new_path2(parent, ctx, path, value, LYD_ANYDATA_STRING, options, node, NULL);
-}
-
static LY_ERR
lyd_new_path_check_find_lypath(struct ly_path *path, const char *str_path, const char *value, uint32_t options)
{
@@ -1623,9 +1616,36 @@
return ret;
}
-API LY_ERR
-lyd_new_path2(struct lyd_node *parent, const struct ly_ctx *ctx, const char *path, const void *value,
- LYD_ANYDATA_VALUETYPE value_type, uint32_t options, struct lyd_node **new_parent, struct lyd_node **new_node)
+/**
+ * @brief Create a new node in the data tree based on a path. All node types can be created.
+ *
+ * If @p path points to a list key and the list instance does not exist, the key value from the predicate is used
+ * and @p value is ignored. Also, if a leaf-list is being created and both a predicate is defined in @p path
+ * and @p value is set, the predicate is preferred.
+ *
+ * For key-less lists and state leaf-lists, positional predicates can be used. If no preciate is used for these
+ * nodes, they are always created.
+ *
+ * @param[in] parent Data parent to add to/modify, can be NULL. Note that in case a first top-level sibling is used,
+ * it may no longer be first if @p path is absolute and starts with a non-existing top-level node inserted
+ * before @p parent. Use ::lyd_first_sibling() to adjust @p parent in these cases.
+ * @param[in] ctx libyang context, must be set if @p parent is NULL.
+ * @param[in] ext Extension instance where the node being created is defined. This argument takes effect only for absolute
+ * path or when the relative paths touches document root (top-level). In such cases the present extension instance replaces
+ * searching for the appropriate module.
+ * @param[in] path [Path](@ref howtoXPath) to create.
+ * @param[in] value Value of the new leaf/leaf-list (const char *) or anyxml/anydata (expected type depends on @p value_type).
+ * For other node types, it is ignored.
+ * @param[in] value_type Anyxml/anydata node @p value type.
+ * @param[in] options Bitmask of options, see @ref pathoptions.
+ * @param[out] new_parent Optional first parent node created. If only one node was created, equals to @p new_node.
+ * @param[out] new_node Optional last node created.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_new_path_(struct lyd_node *parent, const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, const char *path,
+ const void *value, LYD_ANYDATA_VALUETYPE value_type, uint32_t options,
+ struct lyd_node **new_parent, struct lyd_node **new_node)
{
LY_ERR ret = LY_SUCCESS, r;
struct lyxp_expr *exp = NULL;
@@ -1634,7 +1654,9 @@
const struct lysc_node *schema;
LY_ARRAY_COUNT_TYPE path_idx = 0, orig_count = 0;
- LY_CHECK_ARG_RET(ctx, parent || ctx, path, (path[0] == '/') || parent, LY_EINVAL);
+ assert(parent || ctx);
+ assert(path);
+ assert((path[0] == '/') || parent);
if (!ctx) {
ctx = LYD_CTX(parent);
@@ -1645,7 +1667,7 @@
LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_SIMPLE, &exp), cleanup);
/* compile path */
- LY_CHECK_GOTO(ret = ly_path_compile(ctx, NULL, parent ? parent->schema : NULL, NULL, exp, LY_PATH_LREF_FALSE,
+ LY_CHECK_GOTO(ret = ly_path_compile(ctx, NULL, parent ? parent->schema : NULL, ext, exp, LY_PATH_LREF_FALSE,
options & LYD_NEW_PATH_OUTPUT ? LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT, LY_PATH_TARGET_MANY, LY_PREF_JSON,
NULL, NULL, &p), cleanup);
@@ -1810,6 +1832,32 @@
return ret;
}
+API LY_ERR
+lyd_new_path(struct lyd_node *parent, const struct ly_ctx *ctx, const char *path, const char *value, uint32_t options,
+ struct lyd_node **node)
+{
+ LY_CHECK_ARG_RET(ctx, parent || ctx, path, (path[0] == '/') || parent, LY_EINVAL);
+ return lyd_new_path_(parent, ctx, NULL, path, value, LYD_ANYDATA_STRING, options, node, NULL);
+}
+
+API LY_ERR
+lyd_new_path2(struct lyd_node *parent, const struct ly_ctx *ctx, const char *path, const void *value,
+ LYD_ANYDATA_VALUETYPE value_type, uint32_t options, struct lyd_node **new_parent, struct lyd_node **new_node)
+{
+ LY_CHECK_ARG_RET(ctx, parent || ctx, path, (path[0] == '/') || parent, LY_EINVAL);
+ return lyd_new_path_(parent, ctx, NULL, path, value, value_type, options, new_parent, new_node);
+}
+
+API LY_ERR
+lyd_new_ext_path(struct lyd_node *parent, const struct lysc_ext_instance *ext, const char *path, const void *value,
+ uint32_t options, struct lyd_node **node)
+{
+ const struct ly_ctx *ctx = ext ? ext->module->ctx : NULL;
+
+ LY_CHECK_ARG_RET(ctx, ext, path, (path[0] == '/') || parent, LY_EINVAL);
+ return lyd_new_path_(parent, ctx, ext, path, value, LYD_ANYDATA_STRING, options, node, NULL);
+}
+
LY_ERR
lyd_new_implicit_r(struct lyd_node *parent, struct lyd_node **first, const struct lysc_node *sparent,
const struct lys_module *mod, struct ly_set *node_when, struct ly_set *node_types, uint32_t impl_opts,
diff --git a/src/tree_data.h b/src/tree_data.h
index c9f3ef8..24d03f8 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -128,7 +128,8 @@
* Note, that in case the node is defined in an extension instance, the functions mentioned above do not work until you
* provide parent where the new node is supposed to be inserted. The reason is that all the functions searches for the
* top-level nodes directly inside modules. To create a top-level node defined in an extension instance, use
- * ::lyd_new_ext_inner(), ::lyd_new_ext_term(), ::lyd_new_ext_any() and ::lyd_new_ext_list() functions.
+ * ::lyd_new_ext_inner(), ::lyd_new_ext_term(), ::lyd_new_ext_any(), ::lyd_new_ext_list() and ::lyd_new_ext_path()
+ * functions.
*
* The [metadata](@ref howtoPluginsExtensionsMetadata) (and attributes in opaq nodes) can be created with ::lyd_new_meta()
* and ::lyd_new_attr().
@@ -177,6 +178,7 @@
* - ::lyd_new_ext_term()
* - ::lyd_new_ext_list()
* - ::lyd_new_ext_any()
+ * - ::lyd_new_ext_path()
*
* - ::lyd_dup_single()
* - ::lyd_dup_siblings()
@@ -1175,6 +1177,8 @@
* @brief Create a new node in the data tree based on a path. If creating anyxml/anydata nodes, ::lyd_new_path2
* should be used instead, this function expects the value as string.
*
+ * If creating data nodes defined inside an extension instance, use ::lyd_new_ext_path().
+ *
* If @p path points to a list key and the list instance does not exist, the key value from the predicate is used
* and @p value is ignored. Also, if a leaf-list is being created and both a predicate is defined in @p path
* and @p value is set, the predicate is preferred.
@@ -1198,6 +1202,8 @@
/**
* @brief Create a new node in the data tree based on a path. All node types can be created.
*
+ * If creating data nodes defined inside an extension instance, use ::lyd_new_ext_path().
+ *
* If @p path points to a list key and the list instance does not exist, the key value from the predicate is used
* and @p value is ignored. Also, if a leaf-list is being created and both a predicate is defined in @p path
* and @p value is set, the predicate is preferred.
@@ -1222,6 +1228,32 @@
LYD_ANYDATA_VALUETYPE value_type, uint32_t options, struct lyd_node **new_parent, struct lyd_node **new_node);
/**
+ * @brief Create a new node defined in the given extension instance. In case of anyxml/anydata nodes, this function expects
+ * the @p value as string.
+ *
+ * If creating data nodes defined in a module's standard tree, use ::lyd_new_path() or ::lyd_new_path2().
+ *
+ * If @p path points to a list key and the list instance does not exist, the key value from the predicate is used
+ * and @p value is ignored. Also, if a leaf-list is being created and both a predicate is defined in @p path
+ * and @p value is set, the predicate is preferred.
+ *
+ * For key-less lists and state leaf-lists, positional predicates can be used. If no preciate is used for these
+ * nodes, they are always created.
+ *
+ * @param[in] parent Data parent to add to/modify, can be NULL. Note that in case a first top-level sibling is used,
+ * it may no longer be first if @p path is absolute and starts with a non-existing top-level node inserted
+ * before @p parent. Use ::lyd_first_sibling() to adjust @p parent in these cases.
+ * @param[in] ext Extension instance where the node being created is defined.
+ * @param[in] path [Path](@ref howtoXPath) to create.
+ * @param[in] value Value of the new leaf/leaf-list. For other node types, it is ignored.
+ * @param[in] options Bitmask of options, see @ref pathoptions.
+ * @param[out] node Optional first created node.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_new_ext_path(struct lyd_node *parent, const struct lysc_ext_instance *ext, const char *path, const void *value,
+ uint32_t options, struct lyd_node **node);
+
+/**
* @ingroup datatree
* @defgroup implicitoptions Implicit node creation options
*
@@ -1821,7 +1853,7 @@
*/
typedef enum {
LYD_PATH_STD, /**< Generic data path used for logging, node searching (::lyd_find_xpath(), ::lys_find_path()) as well as
- creating new nodes (::lyd_new_path(), ::lyd_new_path2()). */
+ creating new nodes (::lyd_new_path(), ::lyd_new_path2(), ::lyd_new_ext_path()). */
LYD_PATH_STD_NO_LAST_PRED /**< Similar to ::LYD_PATH_STD except there is never a predicate on the last node. While it
can be used to search for nodes, do not use it to create new data nodes (lists). */
} LYD_PATH_TYPE;
diff --git a/tests/utests/data/test_new.c b/tests/utests/data/test_new.c
index 1c66543..314bed4 100644
--- a/tests/utests/data/test_new.c
+++ b/tests/utests/data/test_new.c
@@ -29,22 +29,23 @@
" type string;\n"
" }\n"
" leaf c {\n"
- " type string;}\n"
- " }\n"
- " leaf foo {\n"
- " type uint16;\n"
- " }\n"
- " leaf-list ll {\n"
" type string;\n"
" }\n"
- " container c {\n"
- " leaf-list x {\n"
+ " }\n"
+ " leaf foo {\n"
+ " type uint16;\n"
+ " }\n"
+ " leaf-list ll {\n"
+ " type string;\n"
+ " }\n"
+ " container c {\n"
+ " leaf-list x {\n"
" type string;\n"
" }\n"
- " }"
+ " }\n"
" anydata any {\n"
" config false;\n"
- " }"
+ " }\n"
" list l2 {\n"
" config false;\n"
" container c {\n"
@@ -258,6 +259,45 @@
lyd_free_tree(root);
}
+static void
+test_path_ext(void **state)
+{
+ LY_ERR ret;
+ struct lyd_node *root, *node;
+ const struct lys_module *mod;
+ const char *mod_str = "module ext {yang-version 1.1; namespace urn:tests:extensions:ext; prefix e;"
+ "import ietf-restconf {revision-date 2017-01-26; prefix rc;}"
+ "rc:yang-data template {container c {leaf x {type string;} leaf y {type string;} leaf z {type string;}}}}";
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_DIR_MODULES_YANG));
+ assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "ietf-restconf", "2017-01-26", NULL));
+
+ UTEST_ADD_MODULE(mod_str, LYS_IN_YANG, NULL, &mod);
+
+ /* create x */
+ ret = lyd_new_ext_path(NULL, &mod->compiled->exts[0], "/ext:c/x", "xxx", 0, &root);
+ assert_int_equal(ret, LY_SUCCESS);
+ assert_non_null(root);
+ assert_string_equal(root->schema->name, "c");
+ assert_non_null(node = lyd_child(root));
+ assert_string_equal(node->schema->name, "x");
+ assert_string_equal("xxx", LYD_CANON_VALUE(node));
+
+ /* append y */
+ ret = lyd_new_ext_path(root, &mod->compiled->exts[0], "/ext:c/y", "yyy", 0, &node);
+ assert_int_equal(ret, LY_SUCCESS);
+ assert_string_equal(node->schema->name, "y");
+ assert_string_equal("yyy", LYD_CANON_VALUE(node));
+
+ /* append z */
+ ret = lyd_new_path(root, NULL, "ext:z", "zzz", 0, &node);
+ assert_int_equal(ret, LY_SUCCESS);
+ assert_string_equal(node->schema->name, "z");
+ assert_string_equal("zzz", LYD_CANON_VALUE(node));
+
+ lyd_free_tree(root);
+}
+
int
main(void)
{
@@ -265,6 +305,7 @@
UTEST(test_top_level),
UTEST(test_opaq),
UTEST(test_path),
+ UTEST(test_path_ext),
};
return cmocka_run_group_tests(tests, NULL, NULL);