schema compile UPDATE support all simple text statements
... in extensions.
Fixes #1905
diff --git a/src/schema_compile.c b/src/schema_compile.c
index 5d29adc..f4aae9e 100644
--- a/src/schema_compile.c
+++ b/src/schema_compile.c
@@ -509,7 +509,7 @@
LIBYANG_API_DEF LY_ERR
lys_compile_extension_instance(struct lysc_ctx *ctx, const struct lysp_ext_instance *ext_p, struct lysc_ext_instance *ext)
{
- LY_ERR ret = LY_SUCCESS, r;
+ LY_ERR rc = LY_SUCCESS, r;
LY_ARRAY_COUNT_TYPE u;
struct lysp_stmt *stmt;
void *parsed = NULL, **compiled = NULL;
@@ -527,7 +527,7 @@
if (u == LY_ARRAY_COUNT(ext->substmts)) {
LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG, "Invalid keyword \"%s\" as a child of \"%s%s%s\" extension instance.",
stmt->stmt, ext_p->name, ext_p->argument ? " " : "", ext_p->argument ? ext_p->argument : "");
- ret = LY_EVALID;
+ rc = LY_EVALID;
goto cleanup;
}
}
@@ -564,12 +564,8 @@
case LY_STMT_RPC:
case LY_STMT_USES:
if (!ext_p->parsed) {
- struct lysp_ext_instance *unconst_ext_p;
-
- r = lysp_stmt_parse(ctx, stmt, &parsed, NULL);
- LY_CHECK_ERR_GOTO(r, ret = r, cleanup);
- unconst_ext_p = (struct lysp_ext_instance *)ext_p;
- unconst_ext_p->parsed = parsed;
+ LY_CHECK_GOTO(rc = lysp_stmt_parse(ctx, stmt, &parsed, NULL), cleanup);
+ ((struct lysp_ext_instance *)ext_p)->parsed = parsed;
} else {
struct lysp_node *node, *last_node = NULL;
@@ -578,16 +574,21 @@
last_node = node;
}
/* create and link sibling */
- r = lysp_stmt_parse(ctx, stmt, &parsed, NULL);
- LY_CHECK_ERR_GOTO(r, ret = r, cleanup);
+ LY_CHECK_GOTO(rc = lysp_stmt_parse(ctx, stmt, &parsed, NULL), cleanup);
last_node->next = parsed;
}
/* set storage as an alternative document root in the compile context */
- r = lys_compile_node(ctx, parsed, NULL, 0, NULL);
- LY_CHECK_ERR_GOTO(r, ret = r, cleanup);
+ LY_CHECK_GOTO(rc = lys_compile_node(ctx, parsed, NULL, 0, NULL), cleanup);
break;
+ case LY_STMT_CONTACT:
case LY_STMT_DESCRIPTION:
+ case LY_STMT_ERROR_APP_TAG:
+ case LY_STMT_ERROR_MESSAGE:
+ case LY_STMT_KEY:
+ case LY_STMT_NAMESPACE:
+ case LY_STMT_ORGANIZATION:
+ case LY_STMT_PRESENCE:
case LY_STMT_REFERENCE:
case LY_STMT_UNITS: {
const char **str_p;
@@ -596,7 +597,7 @@
/* single item */
if (*((const char **)ext->substmts[u].storage)) {
LOGVAL(ctx->ctx, LY_VCODE_DUPSTMT, stmt->stmt);
- ret = LY_EVALID;
+ rc = LY_EVALID;
goto cleanup;
}
str_p = (const char **)ext->substmts[u].storage;
@@ -604,21 +605,21 @@
/* sized array */
const char ***strings_array = (const char ***)ext->substmts[u].storage;
- LY_ARRAY_NEW_GOTO(ctx->ctx, *strings_array, str_p, ret, cleanup);
+ LY_ARRAY_NEW_GOTO(ctx->ctx, *strings_array, str_p, rc, cleanup);
}
- r = lydict_insert(ctx->ctx, stmt->arg, 0, str_p);
- LY_CHECK_ERR_GOTO(r, ret = r, cleanup);
+
+ /* called instead of lysp_stmt_parse() to skip validation and not parse nested ext instances */
+ LY_CHECK_GOTO(rc = lydict_insert(ctx->ctx, stmt->arg, 0, str_p), cleanup);
break;
}
case LY_STMT_IF_FEATURE: {
ly_bool enabled;
- r = lysp_stmt_parse(ctx, stmt, &parsed, NULL);
- LY_CHECK_ERR_GOTO(r, ret = r, cleanup);
+ LY_CHECK_GOTO(rc = lysp_stmt_parse(ctx, stmt, &parsed, NULL), cleanup);
r = lys_eval_iffeatures(ctx->ctx, parsed, &enabled);
FREE_ARRAY(ctx->ctx, (struct lysp_qname *)parsed, lysp_qname_free);
- LY_CHECK_ERR_GOTO(r, ret = r, cleanup);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
if (!enabled) {
/* it is disabled, remove the whole extension instance */
return LY_ENOT;
@@ -626,8 +627,12 @@
break;
}
case LY_STMT_STATUS:
- assert(ext->substmts[u].cardinality < LY_STMT_CARD_SOME);
- LY_CHECK_ERR_GOTO(r = lysp_stmt_parse(ctx, stmt, &ext->substmts[u].storage, /* TODO */ NULL), ret = r, cleanup);
+ if (ext->substmts[u].cardinality > LY_STMT_CARD_MAND) {
+ /* only cardinality 0..1 and 1 */
+ goto not_supported;
+ }
+ /* result needs to be a pointer to pointer */
+ LY_CHECK_GOTO(rc = lysp_stmt_parse(ctx, stmt, &ext->substmts[u].storage, NULL), cleanup);
break;
case LY_STMT_TYPE: {
uint16_t *flags = lys_compile_extension_instance_storage(LY_STMT_STATUS, ext->substmts);
@@ -637,7 +642,7 @@
/* single item */
if (*(struct lysc_type **)ext->substmts[u].storage) {
LOGVAL(ctx->ctx, LY_VCODE_DUPSTMT, stmt->stmt);
- ret = LY_EVALID;
+ rc = LY_EVALID;
goto cleanup;
}
compiled = ext->substmts[u].storage;
@@ -645,25 +650,26 @@
/* sized array */
struct lysc_type ***types = (struct lysc_type ***)ext->substmts[u].storage, **type = NULL;
- LY_ARRAY_NEW_GOTO(ctx->ctx, *types, type, ret, cleanup);
+ LY_ARRAY_NEW_GOTO(ctx->ctx, *types, type, rc, cleanup);
compiled = (void *)type;
}
- r = lysp_stmt_parse(ctx, stmt, &parsed, NULL);
- LY_CHECK_ERR_GOTO(r, ret = r, cleanup);
+ LY_CHECK_GOTO(rc = lysp_stmt_parse(ctx, stmt, &parsed, NULL), cleanup);
r = lys_compile_type(ctx, NULL, flags ? *flags : 0, ext_p->name, parsed, (struct lysc_type **)compiled,
- units && !*units ? units : NULL, NULL);
+ (units && !*units) ? units : NULL, NULL);
lysp_type_free(ctx->ctx, parsed);
free(parsed);
- LY_CHECK_ERR_GOTO(r, ret = r, cleanup);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
break;
}
/* TODO support other substatements (parse stmt to lysp and then compile lysp to lysc),
* also note that in many statements their extensions are not taken into account */
default:
- LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG, "Statement \"%s\" is not supported as an extension (found in \"%s%s%s\") substatement.",
- stmt->stmt, ext_p->name, ext_p->argument ? " " : "", ext_p->argument ? ext_p->argument : "");
- ret = LY_EVALID;
+not_supported:
+ LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG, "Statement \"%s\" in cardinality %s is not supported as an extension "
+ "(found in \"%s%s%s\") substatement.", stmt->stmt, ly_cardinality2str(ext->substmts[u].cardinality),
+ ext_p->name, ext_p->argument ? " " : "", ext_p->argument ? ext_p->argument : "");
+ rc = LY_EVALID;
goto cleanup;
}
}
@@ -671,15 +677,16 @@
if (((ext->substmts[u].cardinality == LY_STMT_CARD_MAND) || (ext->substmts[u].cardinality == LY_STMT_CARD_SOME)) && !stmt_counter) {
LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG, "Missing mandatory keyword \"%s\" as a child of \"%s%s%s\".",
- ly_stmt2str(ext->substmts[u].stmt), ext_p->name, ext_p->argument ? " " : "", ext_p->argument ? ext_p->argument : "");
- ret = LY_EVALID;
+ ly_stmt2str(ext->substmts[u].stmt), ext_p->name, ext_p->argument ? " " : "",
+ ext_p->argument ? ext_p->argument : "");
+ rc = LY_EVALID;
goto cleanup;
}
}
cleanup:
ctx->ext = NULL;
- return ret;
+ return rc;
}
/**
diff --git a/src/tree_schema.c b/src/tree_schema.c
index 6e4caa4..4a247cb 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -137,6 +137,23 @@
}
}
+LIBYANG_API_DEF const char *
+ly_cardinality2str(enum ly_stmt_cardinality card)
+{
+ switch (card) {
+ case LY_STMT_CARD_OPT:
+ return "0..1";
+ case LY_STMT_CARD_MAND:
+ return "1";
+ case LY_STMT_CARD_SOME:
+ return "1..n";
+ case LY_STMT_CARD_ANY:
+ return "0..n";
+ }
+
+ return NULL;
+}
+
const char * const ly_devmod_list[] = {
[LYS_DEV_NOT_SUPPORTED] = "not-supported",
[LYS_DEV_ADD] = "add",
diff --git a/src/tree_schema.h b/src/tree_schema.h
index 1376071..8dfd965 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -430,19 +430,13 @@
/**
* @brief Stringify statement identifier.
+ *
* @param[in] stmt The statement identifier to stringify.
* @return Constant string representation of the given @p stmt.
*/
LIBYANG_API_DECL const char *ly_stmt2str(enum ly_stmt stmt);
/**
- * @brief Convert nodetype to statement identifier
- * @param[in] nodetype Nodetype to convert.
- * @return Statement identifier representing the given @p nodetype.
- */
-LIBYANG_API_DECL enum ly_stmt lys_nodetype2stmt(uint16_t nodetype);
-
-/**
* @brief Possible cardinalities of the YANG statements.
*
* Used in extensions plugins to define cardinalities of the extension instance substatements.
@@ -455,6 +449,22 @@
};
/**
+ * @brief Stringify statement cardinality.
+ *
+ * @param[in] card The cardinality to stringify.
+ * @return Constant string representation of the given @p card.
+ */
+LIBYANG_API_DECL const char *ly_cardinality2str(enum ly_stmt_cardinality card);
+
+/**
+ * @brief Convert nodetype to statement identifier
+ *
+ * @param[in] nodetype Nodetype to convert.
+ * @return Statement identifier representing the given @p nodetype.
+ */
+LIBYANG_API_DECL enum ly_stmt lys_nodetype2stmt(uint16_t nodetype);
+
+/**
* @brief YANG import-stmt
*/
struct lysp_import {
diff --git a/tests/utests/schema/test_schema.c b/tests/utests/schema/test_schema.c
index 949ffdf..4aab3a6 100644
--- a/tests/utests/schema/test_schema.c
+++ b/tests/utests/schema/test_schema.c
@@ -59,6 +59,7 @@
/* test_schema_extensions.c */
void test_extension_argument(void **state);
void test_extension_argument_element(void **state);
+void test_extension_compile(void **state);
int
main(void)
@@ -84,6 +85,7 @@
/** test_schema_extensions.c */
UTEST(test_extension_argument),
UTEST(test_extension_argument_element),
+ UTEST(test_extension_compile),
};
return cmocka_run_group_tests(tests, NULL, NULL);
diff --git a/tests/utests/schema/test_schema_extensions.c b/tests/utests/schema/test_schema_extensions.c
index fecc5e6..c82dbb4 100644
--- a/tests/utests/schema/test_schema_extensions.c
+++ b/tests/utests/schema/test_schema_extensions.c
@@ -17,6 +17,7 @@
#include "context.h"
#include "log.h"
+#include "plugins_exts.h"
#include "tree_schema.h"
void
@@ -286,3 +287,66 @@
"/x:{extension='a:e'}");
}
+
+void
+test_extension_compile(void **state)
+{
+ struct lys_module *mod;
+ struct lysc_ctx cctx = {0};
+ struct lysp_ext_instance ext_p = {0};
+ struct lysp_stmt child = {0};
+ struct lysc_ext_instance ext_c = {0};
+ struct lysc_ext_substmt *substmt;
+ LY_ERR rc = LY_SUCCESS;
+
+ /* current module, whatever */
+ mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "yang");
+ assert_true(mod);
+
+ /* compile context */
+ cctx.ctx = UTEST_LYCTX;
+ cctx.cur_mod = mod;
+ cctx.pmod = mod->parsed;
+ cctx.path_len = 1;
+ cctx.path[0] = '/';
+
+ /* parsed ext instance */
+ lydict_insert(UTEST_LYCTX, "pref:my-ext", 0, &ext_p.name);
+ ext_p.format = LY_VALUE_JSON;
+ ext_p.parent_stmt = LY_STMT_MODULE;
+
+ /* compiled ext instance */
+ ext_c.parent_stmt = ext_p.parent_stmt;
+ // ext_c.parent =
+ LY_ARRAY_NEW_GOTO(UTEST_LYCTX, ext_c.substmts, substmt, rc, cleanup);
+
+ /*
+ * error-message
+ */
+ ext_p.child = &child;
+ lydict_insert(UTEST_LYCTX, "error-message", 0, &child.stmt);
+ lydict_insert(UTEST_LYCTX, "my error", 0, &child.arg);
+ child.format = LY_VALUE_JSON;
+ child.kw = LY_STMT_ERROR_MESSAGE;
+
+ substmt->stmt = LY_STMT_ERROR_MESSAGE;
+ substmt->cardinality = LY_STMT_CARD_OPT;
+ substmt->storage = &ext_c.data;
+
+ /* compile */
+ assert_int_equal(LY_SUCCESS, lys_compile_extension_instance(&cctx, &ext_p, &ext_c));
+
+ /* check */
+ assert_string_equal(ext_c.data, "my error");
+
+cleanup:
+ lydict_remove(UTEST_LYCTX, ext_p.name);
+ lydict_remove(UTEST_LYCTX, child.stmt);
+ lydict_remove(UTEST_LYCTX, child.arg);
+ LY_ARRAY_FREE(ext_c.substmts);
+
+ lydict_remove(UTEST_LYCTX, ext_c.data);
+ if (rc) {
+ fail();
+ }
+}