data parsers CHANGE simplify API of data parser functions
Adds some checks regarding data set type and prepares code for support
of RPCs/actions and Notifications.
diff --git a/src/parser_xml.c b/src/parser_xml.c
index 48ddf73..fd88495 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -200,6 +200,22 @@
/* allocate new node */
switch (snode->nodetype) {
+ case LYS_ACTION:
+ if ((ctx->options & LYD_OPT_TYPEMASK) != LYD_OPT_RPC) {
+ LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_RESTRICTION, "Unexpected RPC/action element \"%.*s\" in %s data set.",
+ name_len, name, lyd_parse_options_type2str(ctx->options & LYD_OPT_TYPEMASK));
+ goto cleanup;
+ }
+ cur = calloc(1, sizeof(struct lyd_node_inner));
+ break;
+ case LYS_NOTIF:
+ if ((ctx->options & LYD_OPT_TYPEMASK) != LYD_OPT_RPC) {
+ LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_RESTRICTION, "Unexpected Notification element \"%.*s\" in %s data set.",
+ name_len, name, lyd_parse_options_type2str(ctx->options));
+ goto cleanup;
+ }
+ cur = calloc(1, sizeof(struct lyd_node_inner));
+ break;
case LYS_CONTAINER:
case LYS_LIST:
cur = calloc(1, sizeof(struct lyd_node_inner));
@@ -212,7 +228,6 @@
case LYS_ANYXML:
cur = calloc(1, sizeof(struct lyd_node_any));
break;
- /* TODO LYS_ACTION, LYS_NOTIF */
default:
LOGINT(ctx->ctx);
goto cleanup;
@@ -333,22 +348,39 @@
/* TODO context validation */
}
+ /* TODO add missing siblings default elements */
+
cleanup:
lyd_free_attr(ctx->ctx, attributes, 1);
return ret;
}
LY_ERR
-lyd_parse_xml(struct ly_ctx *ctx, const char *data, int options, struct lyd_node **result)
+lyd_parse_xml(struct ly_ctx *ctx, const char *data, int options, const struct lyd_node **trees, struct lyd_node **result)
{
LY_ERR ret;
+ struct lyd_node_inner *parent = NULL;
struct lyd_xml_ctx xmlctx = {0};
xmlctx.options = options;
xmlctx.ctx = ctx;
xmlctx.line = 1;
- ret = lydxml_nodes(&xmlctx, NULL, &data, result);
+ /* init */
+ *result = NULL;
+
+ if (!data || !data[0]) {
+ goto no_data;
+ }
+
+ if (options & LYD_OPT_RPCREPLY) {
+ /* TODO prepare container for RPC reply, for which we need RPC
+ * - prepare *result as top-level node
+ * - prepare parent as the RPC/action node */
+ (void)trees;
+ }
+
+ ret = lydxml_nodes(&xmlctx, parent, &data, *result ? &parent->child : result);
if (ret) {
lyd_free_all(*result);
*result = NULL;
@@ -356,22 +388,35 @@
/* finish incompletely validated terminal values */
for (unsigned int u = 0; u < xmlctx.incomplete_type_validation.count; u++) {
struct lyd_node_term *node = (struct lyd_node_term*)xmlctx.incomplete_type_validation.objs[u];
- const struct lyd_node **trees = NULL;
+ const struct lyd_node **result_trees = NULL;
/* prepare sized array for validator */
if (*result) {
- trees = lyd_trees_new(1, *result);
+ result_trees = lyd_trees_new(1, *result);
}
/* validate and store the value of the node */
ret = lyd_value_parse(node, node->value.canonized, node->value.canonized ? strlen(node->value.canonized) : 0, 0, 1,
- lydxml_resolve_prefix, ctx, LYD_XML, trees);
- lyd_trees_free(trees, 0);
+ lydxml_resolve_prefix, ctx, LYD_XML, result_trees);
+ lyd_trees_free(result_trees, 0);
if (ret) {
lyd_free_all(*result);
*result = NULL;
break;
}
}
+
+ if (!(*result)) {
+no_data:
+ /* no data */
+ if (options & (LYD_OPT_RPC | LYD_OPT_NOTIF)) {
+ /* error, top level node identify RPC and Notification */
+ LOGERR(ctx, LY_EINVAL, "Invalid input data of data parser - expected %s which cannot be empty.",
+ lyd_parse_options_type2str(options));
+ } else {
+ /* others - no work is needed, just check for missing mandatory nodes */
+ /* TODO lyd_validate(&result, options, ctx); */
+ }
+ }
}
ly_set_erase(&xmlctx.incomplete_type_validation, NULL);
diff --git a/src/tree_data.c b/src/tree_data.c
index bcb72ca..096b7a3 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -89,6 +89,19 @@
return NULL;
}
+API const struct lyd_node **
+lyd_trees_add(const struct lyd_node **trees, const struct lyd_node *tree)
+{
+ const struct lyd_node **t = NULL;
+
+ LY_CHECK_ARG_RET(NULL, tree, trees, trees);
+
+ LY_ARRAY_NEW_RET(tree->schema->module->ctx, trees, t, NULL);
+ *t = lyd_trees_getstart(tree);
+
+ return trees;
+}
+
static int
cmp_str(const char *refstr, const char *str, size_t str_len)
{
@@ -293,53 +306,30 @@
return ret;
}
-static struct lyd_node *
-lyd_parse_mem_(struct ly_ctx *ctx, const char *data, LYD_FORMAT format, int options, va_list ap)
+API struct lyd_node *
+lyd_parse_mem(struct ly_ctx *ctx, const char *data, LYD_FORMAT format, int options, const struct lyd_node **trees)
{
struct lyd_node *result = NULL;
- const struct lyd_node *rpc_act = NULL, *data_tree = NULL, *iter;
#if 0
const char *yang_data_name = NULL;
#endif
- if (lyd_parse_check_options(ctx, options, __func__)) {
+ LY_CHECK_ARG_RET(ctx, ctx, NULL);
+
+ if (lyd_parse_options_check(ctx, options, __func__)) {
return NULL;
}
if (options & LYD_OPT_RPCREPLY) {
- rpc_act = va_arg(ap, const struct lyd_node *);
- if (!rpc_act || rpc_act->parent || !(rpc_act->schema->nodetype & (LYS_ACTION | LYS_LIST | LYS_CONTAINER))) {
- LOGERR(ctx, LY_EINVAL, "Data parser invalid variable parameter (const struct lyd_node *rpc_act).");
+ /* first item in trees is mandatory - the RPC/action request */
+ LY_CHECK_ARG_RET(ctx, trees, LY_ARRAY_SIZE(trees) >= 1, NULL);
+ if (!trees[0] || trees[0]->parent || !(trees[0]->schema->nodetype & (LYS_ACTION | LYS_LIST | LYS_CONTAINER))) {
+ LOGERR(ctx, LY_EINVAL, "Data parser invalid argument trees - the first item in the array must be the RPC/action request when parsing %s.",
+ lyd_parse_options_type2str(options));
return NULL;
}
}
- if (options & (LYD_OPT_RPC | LYD_OPT_NOTIF | LYD_OPT_RPCREPLY)) {
- data_tree = va_arg(ap, const struct lyd_node *);
- if (data_tree) {
- if (options & LYD_OPT_NOEXTDEPS) {
- LOGERR(ctx, LY_EINVAL, "%s: invalid parameter (variable arg const struct lyd_node *data_tree and LYD_OPT_NOEXTDEPS set).",
- __func__);
- return NULL;
- }
- LY_LIST_FOR(data_tree, iter) {
- if (iter->parent) {
- /* a sibling is not top-level */
- LOGERR(ctx, LY_EINVAL, "%s: invalid variable parameter (const struct lyd_node *data_tree).", __func__);
- return NULL;
- }
- }
-
- /* move it to the beginning */
- for (; data_tree->prev->next; data_tree = data_tree->prev);
-
- /* LYD_OPT_NOSIBLINGS cannot be set in this case */
- if (options & LYD_OPT_NOSIBLINGS) {
- LOGERR(ctx, LY_EINVAL, "%s: invalid parameter (variable arg const struct lyd_node *data_tree with LYD_OPT_NOSIBLINGS).", __func__);
- return NULL;
- }
- }
- }
#if 0
if (options & LYD_OPT_DATA_TEMPLATE) {
yang_data_name = va_arg(ap, const char *);
@@ -352,14 +342,14 @@
switch (format) {
case LYD_XML:
- lyd_parse_xml(ctx, data, options, &result);
+ lyd_parse_xml(ctx, data, options, trees, &result);
break;
#if 0
case LYD_JSON:
- lyd_parse_json(ctx, data, options, rpc_act, data_tree, yang_data_name);
+ lyd_parse_json(ctx, data, options, trees, &result);
break;
case LYD_LYB:
- lyd_parse_lyb(ctx, data, options, data_tree, yang_data_name, NULL);
+ lyd_parse_lyb(ctx, data, options, trees, &result);
break;
#endif
case LYD_UNKNOWN:
@@ -371,20 +361,7 @@
}
API struct lyd_node *
-lyd_parse_mem(struct ly_ctx *ctx, const char *data, LYD_FORMAT format, int options, ...)
-{
- va_list ap;
- struct lyd_node *result;
-
- va_start(ap, options);
- result = lyd_parse_mem_(ctx, data, format, options, ap);
- va_end(ap);
-
- return result;
-}
-
-static struct lyd_node *
-lyd_parse_fd_(struct ly_ctx *ctx, int fd, LYD_FORMAT format, int options, va_list ap)
+lyd_parse_fd(struct ly_ctx *ctx, int fd, LYD_FORMAT format, int options, const struct lyd_node **trees)
{
struct lyd_node *result;
size_t length;
@@ -397,7 +374,7 @@
}
LY_CHECK_RET(ly_mmap(ctx, fd, &length, (void **)&addr), NULL);
- result = lyd_parse_mem_(ctx, addr ? addr : "", format, options, ap);
+ result = lyd_parse_mem(ctx, addr ? addr : "", format, options, trees);
if (addr) {
ly_munmap(addr, length);
}
@@ -406,25 +383,11 @@
}
API struct lyd_node *
-lyd_parse_fd(struct ly_ctx *ctx, int fd, LYD_FORMAT format, int options, ...)
-{
- struct lyd_node *ret;
- va_list ap;
-
- va_start(ap, options);
- ret = lyd_parse_fd_(ctx, fd, format, options, ap);
- va_end(ap);
-
- return ret;
-}
-
-API struct lyd_node *
-lyd_parse_path(struct ly_ctx *ctx, const char *path, LYD_FORMAT format, int options, ...)
+lyd_parse_path(struct ly_ctx *ctx, const char *path, LYD_FORMAT format, int options, const struct lyd_node **trees)
{
int fd;
struct lyd_node *result;
size_t len;
- va_list ap;
LY_CHECK_ARG_RET(ctx, ctx, path, NULL);
@@ -449,10 +412,7 @@
} /* else still unknown, try later to detect it from the content */
}
- va_start(ap, options);
- result = lyd_parse_fd_(ctx, fd, format, options, ap);
-
- va_end(ap);
+ result = lyd_parse_fd(ctx, fd, format, options, trees);
close(fd);
return result;
diff --git a/src/tree_data.h b/src/tree_data.h
index ba249d2..66c6328 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -221,7 +221,7 @@
};
-#define LYD_NODE_INNER (LYS_CONTAINER|LYS_LIST) /**< Schema nodetype mask for lyd_node_inner */
+#define LYD_NODE_INNER (LYS_CONTAINER|LYS_LIST|LYS_ACTION|LYS_NOTIF) /**< Schema nodetype mask for lyd_node_inner */
#define LYD_NODE_TERM (LYS_LEAF|LYS_LEAFLIST) /**< Schema nodetype mask for lyd_node_term */
#define LYD_NODE_ANY (LYS_ANYDATA) /**< Schema nodetype mask for lyd_node_any */
@@ -273,7 +273,7 @@
};
/**
- * @brief Data node structure for the inner data tree nodes - containers and lists.
+ * @brief Data node structure for the inner data tree nodes - containers, lists, RPCs, actions and Notifications.
*/
struct lyd_node_inner {
uint32_t hash; /**< hash of this particular node (module name + schema name + key string values if list or
@@ -488,29 +488,15 @@
* @param[in] data Serialized data in the specified format.
* @param[in] format Format of the input data to be parsed.
* @param[in] options Parser options, see @ref parseroptions. \p format LYD_LYB uses #LYD_OPT_TRUSTED implicitly.
- * @param[in] ... Variable arguments depend on \p options. If they include:
- * - #LYD_OPT_DATA:
- * - #LYD_OPT_CONFIG:
- * - #LYD_OPT_GET:
- * - #LYD_OPT_GETCONFIG:
- * - #LYD_OPT_EDIT:
- * - no variable arguments expected.
- * - #LYD_OPT_RPC:
- * - #LYD_OPT_NOTIF:
- * - struct lyd_node *data_tree - additional data tree that will be used
- * when checking any "when" or "must" conditions in the parsed tree that require
- * some nodes outside their subtree. It must be a list of top-level elements!
- * - #LYD_OPT_RPCREPLY:
- * - const struct ::lyd_node *rpc_act - pointer to the whole RPC or (top-level) action operation
- * data tree (the request) of the reply.
- * - const struct ::lyd_node *data_tree - additional data tree that will be used
- * when checking any "when" or "must" conditions in the parsed tree that require
- * some nodes outside their subtree. It must be a list of top-level elements!
+ * @param[in] trees ([Sized array](@ref sizedarrays)) of data trees (e.g. when validating RPC/Notification) where the required
+ * data instances (leafref target, instance-identifier, when, must) can be placed. To simply prepare this structure,
+ * use lyd_trees_new(). In case of parsing RPC/action reply (LYD_OPT_RPCREPLY), the first tree in the array MUST be
+ * complete RPC/action data tree (the source request) for the reply.
* @return Pointer to the built data tree or NULL in case of empty \p data. To free the returned structure,
- * use lyd_free(). In these cases, the function sets #ly_errno to LY_SUCCESS. In case of error,
- * #ly_errno contains appropriate error code (see #LY_ERR).
+ * use lyd_free_all().
+ * @return NULL in case of error. The error information can be then obtained using ly_err* functions.
*/
-struct lyd_node *lyd_parse_mem(struct ly_ctx *ctx, const char *data, LYD_FORMAT format, int options, ...);
+struct lyd_node *lyd_parse_mem(struct ly_ctx *ctx, const char *data, LYD_FORMAT format, int options, const struct lyd_node **trees);
/**
* @brief Read (and validate) data from the given file descriptor.
@@ -526,29 +512,15 @@
* @param[in] fd The standard file descriptor of the file containing the data tree in the specified format.
* @param[in] format Format of the input data to be parsed.
* @param[in] options Parser options, see @ref parseroptions. \p format LYD_LYB uses #LYD_OPT_TRUSTED implicitly.
- * @param[in] ... Variable arguments depend on \p options. If they include:
- * - #LYD_OPT_DATA:
- * - #LYD_OPT_CONFIG:
- * - #LYD_OPT_GET:
- * - #LYD_OPT_GETCONFIG:
- * - #LYD_OPT_EDIT:
- * - no variable arguments expected.
- * - #LYD_OPT_RPC:
- * - #LYD_OPT_NOTIF:
- * - struct lyd_node *data_tree - additional data tree that will be used
- * when checking any "when" or "must" conditions in the parsed tree that require
- * some nodes outside their subtree. It must be a list of top-level elements!
- * - #LYD_OPT_RPCREPLY:
- * - const struct ::lyd_node *rpc_act - pointer to the whole RPC or action operation data
- * tree (the request) of the reply.
- * - const struct ::lyd_node *data_tree - additional data tree that will be used
- * when checking any "when" or "must" conditions in the parsed tree that require
- * some nodes outside their subtree. It must be a list of top-level elements!
+ * @param[in] trees ([Sized array](@ref sizedarrays)) of data trees (e.g. when validating RPC/Notification) where the required
+ * data instances (leafref target, instance-identifier, when, must) can be placed. To simply prepare this structure,
+ * use lyd_trees_new(). In case of parsing RPC/action reply (LYD_OPT_RPCREPLY), the first tree in the array MUST be
+ * complete RPC/action data tree (the source request) for the reply.
* @return Pointer to the built data tree or NULL in case of empty file. To free the returned structure,
- * use lyd_free(). In these cases, the function sets #ly_errno to LY_SUCCESS. In case of error,
- * #ly_errno contains appropriate error code (see #LY_ERR).
+ * use lyd_free_all().
+ * @return NULL in case of error. The error information can be then obtained using ly_err* functions.
*/
-struct lyd_node *lyd_parse_fd(struct ly_ctx *ctx, int fd, LYD_FORMAT format, int options, ...);
+struct lyd_node *lyd_parse_fd(struct ly_ctx *ctx, int fd, LYD_FORMAT format, int options, const struct lyd_node **trees);
/**
* @brief Read (and validate) data from the given file path.
@@ -562,29 +534,15 @@
* @param[in] path Path to the file containing the data tree in the specified format.
* @param[in] format Format of the input data to be parsed.
* @param[in] options Parser options, see @ref parseroptions. \p format LYD_LYB uses #LYD_OPT_TRUSTED implicitly.
- * @param[in] ... Variable arguments depend on \p options. If they include:
- * - #LYD_OPT_DATA:
- * - #LYD_OPT_CONFIG:
- * - #LYD_OPT_GET:
- * - #LYD_OPT_GETCONFIG:
- * - #LYD_OPT_EDIT:
- * - no variable arguments expected.
- * - #LYD_OPT_RPC:
- * - #LYD_OPT_NOTIF:
- * - struct lyd_node *data_tree - additional data tree that will be used
- * when checking any "when" or "must" conditions in the parsed tree that require
- * some nodes outside their subtree. It must be a list of top-level elements!
- * - #LYD_OPT_RPCREPLY:
- * - const struct ::lyd_node *rpc_act - pointer to the whole RPC or action operation data
- * tree (the request) of the reply.
- * - const struct ::lyd_node *data_tree - additional data tree that will be used
- * when checking any "when" or "must" conditions in the parsed tree that require
- * some nodes outside their subtree. It must be a list of top-level elements!
+ * @param[in] trees ([Sized array](@ref sizedarrays)) of data trees (e.g. when validating RPC/Notification) where the required
+ * data instances (leafref target, instance-identifier, when, must) can be placed. To simply prepare this structure,
+ * use lyd_trees_new(). In case of parsing RPC/action reply (LYD_OPT_RPCREPLY), the first tree in the array MUST be
+ * complete RPC/action data tree (the source request) for the reply.
* @return Pointer to the built data tree or NULL in case of empty file. To free the returned structure,
- * use lyd_free(). In these cases, the function sets #ly_errno to LY_SUCCESS. In case of error,
- * #ly_errno contains appropriate error code (see #LY_ERR).
+ * use lyd_free_all().
+ * @return NULL in case of error. The error information can be then obtained using ly_err* functions.
*/
-struct lyd_node *lyd_parse_path(struct ly_ctx *ctx, const char *path, LYD_FORMAT format, int options, ...);
+struct lyd_node *lyd_parse_path(struct ly_ctx *ctx, const char *path, LYD_FORMAT format, int options, const struct lyd_node **trees);
/**
* @brief Free all the nodes in the data tree.
@@ -637,6 +595,15 @@
const struct lyd_node **lyd_trees_new(size_t count, const struct lyd_node *tree, ...);
/**
+ * @brief Add tree into the ([sized array](@ref sizedarrays)) of data trees created by lyd_trees_new(),
+ *
+ * @param[in] trees Existing [sized array](@ref sizedarrays)) of data trees to be extended.
+ * @param[in] tree Data tree to be included into the provided @p trees ([sized array](@ref sizedarrays)).
+ * @return NULL in case of memory allocation failure or invalid argument, extended @p trees ([sized array](@ref sizedarrays)) otherwise.
+ */
+const struct lyd_node **lyd_trees_add(const struct lyd_node **trees, const struct lyd_node *tree);
+
+/**
* @brief Free the trees ([sized array](@ref sizedarrays)).
*
* @param[in] trees ([Sized array](@ref sizedarrays)) of data trees.
diff --git a/src/tree_data_helpers.c b/src/tree_data_helpers.c
index 256c442..c85e1f5 100644
--- a/src/tree_data_helpers.c
+++ b/src/tree_data_helpers.c
@@ -53,7 +53,7 @@
}
LY_ERR
-lyd_parse_check_options(struct ly_ctx *ctx, int options, const char *func)
+lyd_parse_options_check(struct ly_ctx *ctx, int options, const char *func)
{
int x = options & LYD_OPT_TYPEMASK;
@@ -82,3 +82,30 @@
return LY_SUCCESS;
}
+
+const char *
+lyd_parse_options_type2str(int options)
+{
+ switch (options & LYD_OPT_TYPEMASK) {
+ case LYD_OPT_DATA:
+ return "complete datastore";
+ case LYD_OPT_CONFIG:
+ return "configuration datastore";
+ case LYD_OPT_GET:
+ return "<get> RPC reply";
+ case LYD_OPT_GETCONFIG:
+ return "<get-config> RPC reply";
+ case LYD_OPT_EDIT:
+ return "<edit-config> configuration";
+ case LYD_OPT_RPC:
+ return "RPC/action input";
+ case LYD_OPT_RPCREPLY:
+ return "RPC/action output";
+ case LYD_OPT_NOTIF:
+ return "Notification";
+ case LYD_OPT_NOTIF_FILTER:
+ return "Notification filter";
+ }
+ LOGINT(NULL);
+ return NULL;
+}
diff --git a/src/tree_data_internal.h b/src/tree_data_internal.h
index 5adeac9..5e4d477 100644
--- a/src/tree_data_internal.h
+++ b/src/tree_data_internal.h
@@ -36,7 +36,14 @@
* @return LY_SUCCESS when options are ok
* @return LY_EINVAL when multiple data types bits are set, or incompatible options are used together.
*/
-LY_ERR lyd_parse_check_options(struct ly_ctx *ctx, int options, const char *func);
+LY_ERR lyd_parse_options_check(struct ly_ctx *ctx, int options, const char *func);
+
+/**
+ * @brief Get string describing the type of the data according to the data parser options.
+ * @param[in] options Data parser options to examine.
+ * @return String description of the data set type.
+ */
+const char *lyd_parse_options_type2str(int options);
/**
* @brief Validate, canonize and store the given @p value into the node according to the node's type's rules.
@@ -65,10 +72,14 @@
* @param[in] ctx libyang context
* @param[in] data Pointer to the XML string representation of the YANG data to parse.
* @param[in] options @ref dataparseroptions
+ * @param[in] trees ([Sized array](@ref sizedarrays)) of data trees (e.g. when validating RPC/Notification) where the required
+ * data instances (leafref target, instance-identifier, when, must) can be placed. To simply prepare this structure,
+ * use lyd_trees_new(). In case of parsing RPC/action reply (LYD_OPT_RPCREPLY), the first tree in the array MUST be
+ * complete RPC/action data tree (the source request) for the reply.
* @param[out] result Resulting list of the parsed data trees. Note that NULL can be a valid result.
* @reutn LY_ERR value.
*/
-LY_ERR lyd_parse_xml(struct ly_ctx *ctx, const char *data, int options, struct lyd_node **result);
+LY_ERR lyd_parse_xml(struct ly_ctx *ctx, const char *data, int options, const struct lyd_node **trees, struct lyd_node **result);
/**
* @defgroup datahash Data nodes hash manipulation
diff --git a/src/tree_schema.h b/src/tree_schema.h
index e8b3f75..004a195 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -67,6 +67,7 @@
#define LYS_ANYDATA 0x0120 /**< anydata statement node, in tests it can be used for both #LYS_ANYXML and #LYS_ANYDATA */
#define LYS_ACTION 0x400 /**< RPC or action */
+#define LYS_RPC LYS_ACTION /**< RPC or action (for backward compatibility) */
#define LYS_NOTIF 0x800
#define LYS_CASE 0x0040 /**< case statement node */