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);