data types FEATURE add printer callback to the type plugins
Direct using of lyd_value's canonized member is dangerous since some
of the data may not have canonical format and plugin doesn't store it.
Therefore there is a need of a generic printer plugins to provide
printable representation of the value for the specific format (XML,
JSON).
diff --git a/src/plugins_types.c b/src/plugins_types.c
index 62e3498..7d2b33b 100644
--- a/src/plugins_types.c
+++ b/src/plugins_types.c
@@ -45,6 +45,22 @@
}
/**
+ * @brief Generic printer callback of the canonized value.
+ *
+ * Implementation of the ly_type_print_clb.
+ */
+static const char *
+ly_type_print_canonical(const struct lyd_value *value, LYD_FORMAT UNUSED(format), int prefixes, int *dynamic)
+{
+ *dynamic = 0;
+ if (prefixes) {
+ return "";
+ } else {
+ return (char*)value->canonized;
+ }
+}
+
+/**
* @brief Free canonized value in lyd_value.
*
* Implementation of the ly_type_free_clb.
@@ -1435,8 +1451,12 @@
if (!lyd_target(storage->target, trees)) {
/* TODO print instance-identifier */
- asprintf(&errmsg, "Invalid instance-identifier \"%s\" value - required instance not found.",
- "TODO");
+ int dynamic = 0;
+ const char *id = storage->realtype->plugin->print(storage, format, 0, &dynamic);
+ asprintf(&errmsg, "Invalid instance-identifier \"%s\" value - required instance not found.", id);
+ if (dynamic) {
+ free((char*)id);
+ }
/* we have to clean up the storage */
type->plugin->free(ctx, storage);
@@ -1805,6 +1825,141 @@
return LY_SUCCESS;
}
+static void
+ly_type_print_prefixes_mod(char **str, struct ly_set *printed, struct lys_module *mod)
+{
+ unsigned int u = printed->count;
+ if (ly_set_add(printed, mod, 0) == (int)u) {
+ /* newly added module - print it */
+ asprintf(str, "%s%sxmlns:%s=\"%s\"", *str ? *str : "", *str ? " " : "", mod->prefix, mod->ns);
+ }
+}
+
+/**
+ * @brief Printer callback printing the instance-identifier value.
+ *
+ * Implementation of the ly_type_print_clb.
+ */
+static const char *
+ly_type_print_instanceid(const struct lyd_value *value, LYD_FORMAT format, int prefixes, int *dynamic)
+{
+ unsigned int u, v;
+ char *result = NULL;
+ struct ly_set printed_prefixes = {0};
+
+ if (prefixes) {
+ /* print namespace definition -only for XML, it does not make sense in JSON */
+ if (format == LYD_XML) {
+ LY_ARRAY_FOR(value->target, u) {
+ ly_type_print_prefixes_mod(&result, &printed_prefixes, value->target[u].node->module);
+ LY_ARRAY_FOR(value->target[u].predicates, v) {
+ struct lyd_value_path_predicate *pred = &value->target[u].predicates[v];
+ if (pred->type) {
+ /* non-position predicate with a value
+ * (key is of the same namespace as the list, so we don't need to handle its prefix) */
+ int d = 0;
+ char *token, *s = (char*)pred->value->realtype->plugin->print(pred->value, LYD_XML, 1, &d);
+ if (s && s[0]) {
+ char *start = s;
+ if (!d) {
+ start = s = strdup(s);
+ }
+ while ((token = strsep(&s, " "))) {
+ unsigned int i;
+ char *equal;
+ equal = strchr(token, '=');
+ /* check if we already have such a prefix */
+ for (i = 0; i < printed_prefixes.count; i++) {
+ if (!strcmp(((struct lys_module *)printed_prefixes.objs[i])->ns, equal + 1)) {
+ /* match, do not print this namespace definition */
+ break;
+ }
+ }
+ if (i == printed_prefixes.count) {
+ /* not yet printed namespace */
+ ly_strcat(&result, "%s%s", result ? " " : "", token);
+ }
+ }
+ free(start);
+ }
+ }
+ }
+ }
+ } else { /* JSON */
+ *dynamic = 0;
+ return "";
+ }
+ } else {
+ /* print value */
+ if (format == LYD_XML) {
+ /* everything is prefixed */
+ LY_ARRAY_FOR(value->target, u) {
+ ly_strcat(&result, "/%s:%s", value->target[u].node->module->prefix, value->target[u].node->name);
+ LY_ARRAY_FOR(value->target[u].predicates, v) {
+ struct lyd_value_path_predicate *pred = &value->target[u].predicates[v];
+ if (pred->type == 0) {
+ /* position predicate */
+ ly_strcat(&result, "[%"PRIu64"]", pred->position);
+ } else if (pred->type == 1) {
+ /* key-predicate */
+ int d = 0;
+ const char *value = pred->value->realtype->plugin->print(pred->value, format, 0, &d);
+ ly_strcat(&result, "[%s:%s='%s']", pred->key->module->prefix, pred->key->name, value);
+ if (d) {
+ free((char*)value);
+ }
+ } else if (pred->type == 2) {
+ /* leaf-list-predicate */
+ int d = 0;
+ const char *value = pred->value->realtype->plugin->print(pred->value, format, 0, &d);
+ ly_strcat(&result, "[.='%s']", value);
+ if (d) {
+ free((char*)value);
+ }
+ }
+ }
+ }
+ } else { /* LYD_JSON */
+ /* only the first node or the node changing module is prefixed */
+ struct lys_module *mod = NULL;
+ LY_ARRAY_FOR(value->target, u) {
+ if (mod != value->target[u].node->module) {
+ mod = value->target[u].node->module;
+ ly_strcat(&result, "/%s:%s", mod->name, value->target[u].node->name);
+ } else {
+ ly_strcat(&result, "/%s", value->target[u].node->name);
+ }
+ LY_ARRAY_FOR(value->target[u].predicates, v) {
+ struct lyd_value_path_predicate *pred = &value->target[u].predicates[v];
+ if (pred->type == 0) {
+ /* position predicate */
+ ly_strcat(&result, "[%"PRIu64"]", pred->position);
+ } else if (pred->type == 1) {
+ /* key-predicate */
+ int d = 0;
+ const char *value = pred->value->realtype->plugin->print(pred->value, format, 0, &d);
+ ly_strcat(&result, "[%s='%s']", pred->key->name, value);
+ if (d) {
+ free((char*)value);
+ }
+ } else if (pred->type == 2) {
+ /* leaf-list-predicate */
+ int d = 0;
+ const char *value = pred->value->realtype->plugin->print(pred->value, format, 0, &d);
+ ly_strcat(&result, "[.='%s']", value);
+ if (d) {
+ free((char*)value);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ *dynamic = 1;
+ return result;
+}
+
/**
* @brief Free value of the YANG built-in instance-identifier types.
*
@@ -2077,6 +2232,17 @@
}
/**
+ * @brief Printer callback printing the leafref value.
+ *
+ * Implementation of the ly_type_print_clb.
+ */
+static const char *
+ly_type_print_leafref(const struct lyd_value *value, LYD_FORMAT format, int prefixes, int *dynamic)
+{
+ return value->realtype->plugin->print(value, format, prefixes, dynamic);
+}
+
+/**
* @brief Free value of the YANG built-in leafref type.
*
* Implementation of the ly_type_free_clb.
@@ -2221,6 +2387,34 @@
}
/**
+ * @brief Printer callback printing the union value.
+ *
+ * Implementation of the ly_type_print_clb.
+ */
+static const char *
+ly_type_print_union(const struct lyd_value *value, LYD_FORMAT format, int prefixes, int *dynamic)
+{
+ unsigned int u;
+ char *result = NULL;
+
+ if (prefixes) {
+ if (format == LYD_XML) {
+ LY_ARRAY_FOR(value->subvalue->prefixes, u) {
+ ly_strcat(&result, "%sxmlns:%s=\"%s\"", result ? " " : "",
+ value->subvalue->prefixes[u].prefix, value->subvalue->prefixes[u].mod->ns);
+ }
+ *dynamic = 1;
+ return result;
+ } else {
+ *dynamic = 0;
+ return "";
+ }
+ } else {
+ return ly_type_print_canonical(value, format, 0, dynamic);
+ }
+}
+
+/**
* @brief Free value of the YANG built-in union type.
*
* Implementation of the ly_type_free_clb.
@@ -2250,23 +2444,23 @@
*/
struct lysc_type_plugin ly_builtin_type_plugins[LY_DATA_TYPE_COUNT] = {
{0}, /* LY_TYPE_UNKNOWN */
- {.type = LY_TYPE_BINARY, .store = ly_type_store_binary, .compare = ly_type_compare_canonical, .free = ly_type_free_canonical},
- {.type = LY_TYPE_UINT8, .store = ly_type_store_uint, .compare = ly_type_compare_canonical, .free = ly_type_free_canonical},
- {.type = LY_TYPE_UINT16, .store = ly_type_store_uint, .compare = ly_type_compare_canonical, .free = ly_type_free_canonical},
- {.type = LY_TYPE_UINT32, .store = ly_type_store_uint, .compare = ly_type_compare_canonical, .free = ly_type_free_canonical},
- {.type = LY_TYPE_UINT64, .store = ly_type_store_uint, .compare = ly_type_compare_canonical, .free = ly_type_free_canonical},
- {.type = LY_TYPE_STRING, .store = ly_type_store_string, .compare = ly_type_compare_canonical, .free = ly_type_free_canonical},
- {.type = LY_TYPE_BITS, .store = ly_type_store_bits, .compare = ly_type_compare_canonical, .free = ly_type_free_bits},
- {.type = LY_TYPE_BOOL, .store = ly_type_store_boolean, .compare = ly_type_compare_canonical, .free = ly_type_free_canonical},
- {.type = LY_TYPE_DEC64, .store = ly_type_store_decimal64, .compare = ly_type_compare_canonical, .free = ly_type_free_canonical},
- {.type = LY_TYPE_EMPTY, .store = ly_type_store_empty, .compare = ly_type_compare_empty, .free = ly_type_free_canonical},
- {.type = LY_TYPE_ENUM, .store = ly_type_store_enum, .compare = ly_type_compare_canonical, .free = ly_type_free_canonical},
- {.type = LY_TYPE_IDENT, .store = ly_type_store_identityref, .compare = ly_type_compare_identityref, .free = ly_type_free_canonical},
- {.type = LY_TYPE_INST, .store = ly_type_store_instanceid, .compare = ly_type_compare_instanceid, .free = ly_type_free_instanceid},
- {.type = LY_TYPE_LEAFREF, .store = ly_type_store_leafref, .compare = ly_type_compare_leafref, .free = ly_type_free_leafref},
- {.type = LY_TYPE_UNION, .store = ly_type_store_union, .compare = ly_type_compare_union, .free = ly_type_free_union},
- {.type = LY_TYPE_INT8, .store = ly_type_store_int, .compare = ly_type_compare_canonical, .free = ly_type_free_canonical},
- {.type = LY_TYPE_INT16, .store = ly_type_store_int, .compare = ly_type_compare_canonical, .free = ly_type_free_canonical},
- {.type = LY_TYPE_INT32, .store = ly_type_store_int, .compare = ly_type_compare_canonical, .free = ly_type_free_canonical},
- {.type = LY_TYPE_INT64, .store = ly_type_store_int, .compare = ly_type_compare_canonical, .free = ly_type_free_canonical},
+ {.type = LY_TYPE_BINARY, .store = ly_type_store_binary, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical, .free = ly_type_free_canonical},
+ {.type = LY_TYPE_UINT8, .store = ly_type_store_uint, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical, .free = ly_type_free_canonical},
+ {.type = LY_TYPE_UINT16, .store = ly_type_store_uint, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical, .free = ly_type_free_canonical},
+ {.type = LY_TYPE_UINT32, .store = ly_type_store_uint, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical, .free = ly_type_free_canonical},
+ {.type = LY_TYPE_UINT64, .store = ly_type_store_uint, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical, .free = ly_type_free_canonical},
+ {.type = LY_TYPE_STRING, .store = ly_type_store_string, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical, .free = ly_type_free_canonical},
+ {.type = LY_TYPE_BITS, .store = ly_type_store_bits, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical, .free = ly_type_free_bits},
+ {.type = LY_TYPE_BOOL, .store = ly_type_store_boolean, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical, .free = ly_type_free_canonical},
+ {.type = LY_TYPE_DEC64, .store = ly_type_store_decimal64, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical, .free = ly_type_free_canonical},
+ {.type = LY_TYPE_EMPTY, .store = ly_type_store_empty, .compare = ly_type_compare_empty, .print = ly_type_print_canonical, .free = ly_type_free_canonical},
+ {.type = LY_TYPE_ENUM, .store = ly_type_store_enum, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical, .free = ly_type_free_canonical},
+ {.type = LY_TYPE_IDENT, .store = ly_type_store_identityref, .compare = ly_type_compare_identityref, .print = ly_type_print_canonical, .free = ly_type_free_canonical},
+ {.type = LY_TYPE_INST, .store = ly_type_store_instanceid, .compare = ly_type_compare_instanceid, .print = ly_type_print_instanceid, .free = ly_type_free_instanceid},
+ {.type = LY_TYPE_LEAFREF, .store = ly_type_store_leafref, .compare = ly_type_compare_leafref, .print = ly_type_print_leafref, .free = ly_type_free_leafref},
+ {.type = LY_TYPE_UNION, .store = ly_type_store_union, .compare = ly_type_compare_union, .print = ly_type_print_union, .free = ly_type_free_union},
+ {.type = LY_TYPE_INT8, .store = ly_type_store_int, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical, .free = ly_type_free_canonical},
+ {.type = LY_TYPE_INT16, .store = ly_type_store_int, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical, .free = ly_type_free_canonical},
+ {.type = LY_TYPE_INT32, .store = ly_type_store_int, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical, .free = ly_type_free_canonical},
+ {.type = LY_TYPE_INT64, .store = ly_type_store_int, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical, .free = ly_type_free_canonical},
};
diff --git a/src/plugins_types.h b/src/plugins_types.h
index a4b243f..3340832 100644
--- a/src/plugins_types.h
+++ b/src/plugins_types.h
@@ -132,6 +132,22 @@
typedef LY_ERR (*ly_type_compare_clb)(const struct lyd_value *val1, const struct lyd_value *val2);
/**
+ * @brief Callback to receive printed (canoncal) value of the data stored in @p value.
+ *
+ * @param[in] value Value to print.
+ * @param[in] format Format in which the data are supposed to be printed.
+ * Only 2 formats are currently implemented: LYD_XML and LYD_JSON.
+ * @param[in] prefixes Flag to get printed prefix/namespace string instead of the value itself.
+ * Since JSON uses YANG module names as prefixes, this option make sense only with LYD_XML @p format.
+ * @param[out] dynamic Flag if the returned string is dynamically allocated. In such a case the caller is responsible
+ * for freeing it.
+ * @return String with the value of @p value in specified @p format. According to the returned @p dynamic flag, caller
+ * can be responsible for freeing allocated memory.
+ * @return NULL in case of error.
+ */
+typedef const char *(*ly_type_print_clb)(const struct lyd_value *value, LYD_FORMAT format, int prefixes, int *dynamic);
+
+/**
* @brief Callback for freeing the user type values stored by ly_type_store_clb().
*
* Note that this callback is responsible also for freeing the canonized member in the @p value.
@@ -152,6 +168,7 @@
LY_DATA_TYPE type; /**< implemented type, use LY_TYPE_UNKNOWN for derived data types */
ly_type_store_clb store; /**< function to validate, canonize and store (according to the options) the value in the type-specific way */
ly_type_compare_clb compare; /**< comparison callback to compare 2 values of the same type */
+ ly_type_print_clb print; /**< printer callback to get string representing the value */
ly_type_free_clb free; /**< optional function to free the type-spceific way stored value */
uint32_t flags; /**< [type flags ](@ref plugintypeflags). */
};
diff --git a/tests/features/test_types.c b/tests/features/test_types.c
index 6d057d0..5075294 100644
--- a/tests/features/test_types.c
+++ b/tests/features/test_types.c
@@ -816,11 +816,11 @@
data = "<cont xmlns=\"urn:tests:types\"/><t:inst xmlns:t=\"urn:tests:types\">/t:cont/t:leaftarget</t:inst>";
assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
- logbuf_assert("Invalid instance-identifier \"TODO\" value - required instance not found. /");
+ logbuf_assert("Invalid instance-identifier \"/t:cont/t:leaftarget\" value - required instance not found. /");
data = "<t:inst xmlns:t=\"urn:tests:types\">/t:cont/t:leaftarget</t:inst>";
assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
- logbuf_assert("Invalid instance-identifier \"TODO\" value - required instance not found. /");
+ logbuf_assert("Invalid instance-identifier \"/t:cont/t:leaftarget\" value - required instance not found. /");
data = "<leaflisttarget xmlns=\"urn:tests:types\">x</leaflisttarget><t:inst xmlns:t=\"urn:tests:types\">/t:leaflisttarget[1</t:inst>";
assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
@@ -845,7 +845,7 @@
data = "<cont xmlns=\"urn:tests:types\"><leaflisttarget>1</leaflisttarget><leaflisttarget>2</leaflisttarget></cont>"
"<t:inst xmlns:t=\"urn:tests:types\">/t:cont/t:leaflisttarget[4]</t:inst>";
assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
- logbuf_assert("Invalid instance-identifier \"TODO\" value - required instance not found. /");
+ logbuf_assert("Invalid instance-identifier \"/t:cont/t:leaflisttarget[4]\" value - required instance not found. /");
data = "<t:inst-noreq xmlns:t=\"urn:tests:types\">/t:cont/t:leaflisttarget[6]</t:inst-noreq>";
assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
@@ -876,7 +876,7 @@
data = "<cont xmlns=\"urn:tests:types\"><leaflisttarget>1</leaflisttarget></cont>"
"<t:inst xmlns:t=\"urn:tests:types\">/t:cont/t:leaflisttarget[.='2']</t:inst>";
assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
- logbuf_assert("Invalid instance-identifier \"TODO\" value - required instance not found. /");
+ logbuf_assert("Invalid instance-identifier \"/t:cont/t:leaflisttarget[.='2']\" value - required instance not found. /");
data = "<cont xmlns=\"urn:tests:types\"><leaflisttarget>1</leaflisttarget></cont>"
"<t:inst xmlns:t=\"urn:tests:types\">/t:cont/t:leaflisttarget[.='x']</t:inst>";
@@ -893,7 +893,7 @@
data = "<cont xmlns=\"urn:tests:types\"><listtarget><id>1</id><value>x</value></listtarget></cont>"
"<t:inst xmlns:t=\"urn:tests:types\">/t:cont/t:listtarget[t:id='2']</t:inst>";
assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
- logbuf_assert("Invalid instance-identifier \"TODO\" value - required instance not found. /");
+ logbuf_assert("Invalid instance-identifier \"/t:cont/t:listtarget[t:id='2']\" value - required instance not found. /");
data = "<leaflisttarget xmlns=\"urn:tests:types\">a</leaflisttarget>"
"<leaflisttarget xmlns=\"urn:tests:types\">b</leaflisttarget>"