data tree FEATURE function for diff reverse
diff --git a/src/diff.c b/src/diff.c
index 9ce3ff5..d0ee123 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -1134,7 +1134,7 @@
switch (diff_match->schema->nodetype) {
case LYS_LIST:
case LYS_LEAFLIST:
- /* it was created/moved somewhere somewhere, but now it will be created/moved somewhere else,
+ /* it was created/moved somewhere, but now it will be created/moved somewhere else,
* keep orig_key/orig_value (only replace oper) and replace key/value */
assert(lysc_is_userordered(diff_match->schema));
meta_name = (diff_match->schema->nodetype == LYS_LIST ? "key" : "value");
@@ -1545,3 +1545,191 @@
{
return lyd_diff_merge_module(src_diff, NULL, NULL, NULL, diff);
}
+
+static LY_ERR
+lyd_diff_reverse_value(struct lyd_node *leaf, const struct lys_module *mod)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyd_meta *meta;
+ const char *val1 = NULL, *val2 = NULL;
+ int dyn1 = 0, dyn2 = 0, flags;
+
+ meta = lyd_find_meta(leaf->meta, mod, "orig-value");
+ LY_CHECK_ERR_RET(!meta, LOGINT(LYD_NODE_CTX(leaf)), LY_EINT);
+
+ /* orig-value */
+ val1 = lyd_meta2str(meta, &dyn1);
+
+ /* current value */
+ val2 = lyd_value2str((struct lyd_node_term *)leaf, &dyn2);
+
+ /* switch values, keep default flag */
+ flags = leaf->flags;
+ LY_CHECK_GOTO(ret = lyd_change_term(leaf, val1), cleanup);
+ leaf->flags = flags;
+ LY_CHECK_GOTO(ret = lyd_change_meta(meta, val2), cleanup);
+
+cleanup:
+ if (dyn1) {
+ free((char *)val1);
+ }
+ if (dyn2) {
+ free((char *)val2);
+ }
+ return ret;
+}
+
+static LY_ERR
+lyd_diff_reverse_default(struct lyd_node *node, const struct lys_module *mod)
+{
+ struct lyd_meta *meta;
+ const char *val;
+ int dyn, flag1, flag2;
+
+ meta = lyd_find_meta(node->meta, mod, "orig-default");
+ if (!meta) {
+ /* default flag did not change */
+ return LY_SUCCESS;
+ }
+
+ /* orig-default */
+ val = lyd_meta2str(meta, &dyn);
+ assert(!dyn);
+ if (!strcmp(val, "true")) {
+ flag1 = LYD_DEFAULT;
+ } else {
+ flag1 = 0;
+ }
+
+ /* current default */
+ flag2 = node->flags & LYD_DEFAULT;
+
+ /* switch defaults */
+ node->flags &= ~LYD_DEFAULT;
+ node->flags |= flag1;
+ LY_CHECK_RET(lyd_change_meta(meta, flag2 ? "true" : "false"));
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+lyd_diff_reverse_meta(struct lyd_node *node, const struct lys_module *mod, const char *name1, const char *name2)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyd_meta *meta1, *meta2;
+ const char *val1 = NULL, *val2 = NULL;
+ int dyn1 = 0, dyn2 = 0;
+
+ meta1 = lyd_find_meta(node->meta, mod, name1);
+ LY_CHECK_ERR_RET(!meta1, LOGINT(LYD_NODE_CTX(node)), LY_EINT);
+
+ meta2 = lyd_find_meta(node->meta, mod, name2);
+ LY_CHECK_ERR_RET(!meta2, LOGINT(LYD_NODE_CTX(node)), LY_EINT);
+
+ /* value1 */
+ val1 = lyd_meta2str(meta1, &dyn1);
+
+ /* value2 */
+ val2 = lyd_meta2str(meta2, &dyn2);
+
+ /* switch values */
+ LY_CHECK_GOTO(ret = lyd_change_meta(meta1, val2), cleanup);
+ LY_CHECK_GOTO(ret = lyd_change_meta(meta2, val1), cleanup);
+
+cleanup:
+ if (dyn1) {
+ free((char *)val1);
+ }
+ if (dyn2) {
+ free((char *)val2);
+ }
+ return ret;
+}
+
+API LY_ERR
+lyd_diff_reverse(const struct lyd_node *src_diff, struct lyd_node **diff)
+{
+ LY_ERR ret = LY_SUCCESS;
+ const struct lys_module *mod;
+ struct lyd_node *root, *next, *elem;
+ enum lyd_diff_op op;
+
+ LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL);
+
+ if (!src_diff) {
+ *diff = NULL;
+ return LY_SUCCESS;
+ }
+
+ /* duplicate diff */
+ *diff = lyd_dup(src_diff, NULL, LYD_DUP_WITH_SIBLINGS | LYD_DUP_RECURSIVE);
+ LY_CHECK_RET(!*diff, LY_EMEM);
+
+ /* find module with metadata needed for later */
+ mod = ly_ctx_get_module_latest(LYD_NODE_CTX(src_diff), "yang");
+ LY_CHECK_ERR_GOTO(!mod, LOGINT(LYD_NODE_CTX(src_diff)); ret = LY_EINT, cleanup);
+
+ LY_LIST_FOR(*diff, root) {
+ LYD_TREE_DFS_BEGIN(root, next, elem) {
+ /* find operation attribute, if any */
+ LY_CHECK_GOTO(ret = lyd_diff_get_op(elem, &op), cleanup);
+
+ switch (op) {
+ case LYD_DIFF_OP_CREATE:
+ /* reverse create to delete */
+ LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_DELETE), cleanup);
+ break;
+ case LYD_DIFF_OP_DELETE:
+ /* reverse delete to create */
+ LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_CREATE), cleanup);
+ break;
+ case LYD_DIFF_OP_REPLACE:
+ switch (elem->schema->nodetype) {
+ case LYS_LEAF:
+ /* leaf value change */
+ LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup);
+ LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
+ break;
+ case LYS_LEAFLIST:
+ /* leaf-list move */
+ LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
+ LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-value", "value"), cleanup);
+ break;
+ case LYS_LIST:
+ /* list move */
+ LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-key", "key"), cleanup);
+ break;
+ default:
+ LOGINT(LYD_NODE_CTX(src_diff));
+ ret = LY_EINT;
+ goto cleanup;
+ }
+ break;
+ case LYD_DIFF_OP_NONE:
+ switch (elem->schema->nodetype) {
+ case LYS_LEAF:
+ case LYS_LEAFLIST:
+ /* default flag change */
+ LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup);
+ break;
+ default:
+ /* nothing to do */
+ break;
+ }
+ break;
+ default:
+ /* nothing to do */
+ break;
+ }
+
+ LYD_TREE_DFS_END(root, next, elem);
+ }
+ }
+
+cleanup:
+ if (ret) {
+ lyd_free_siblings(*diff);
+ *diff = NULL;
+ }
+ return ret;
+}
diff --git a/src/tree_data.h b/src/tree_data.h
index aa223f9..7f40cc7 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -1063,6 +1063,17 @@
LY_ERR lyd_diff_merge(const struct lyd_node *src_diff, struct lyd_node **diff);
/**
+ * @brief Reverse a diff and make the opposite changes. Meaning change create to delete, delete to create,
+ * or move from place A to B to move from B to A and so on.
+ *
+ * @param[in] src_diff Diff to reverse.
+ * @param[out] diff Reversed diff.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR on error.
+ */
+LY_ERR lyd_diff_reverse(const struct lyd_node *src_diff, struct lyd_node **diff);
+
+/**
* @brief Find the target in data of a compiled ly_path structure (instance-identifier).
*
* @param[in] path Compiled path structure.