Skip to content

Leafref util functions #2352

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 28 additions & 2 deletions src/tree_schema.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* @author Michal Vasko <[email protected]>
* @brief libyang representation of YANG schema trees.
*
* Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
* Copyright (c) 2015 - 2025 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
Expand Down Expand Up @@ -1908,13 +1908,39 @@ LIBYANG_API_DECL struct lysc_must *lysc_node_musts(const struct lysc_node *node)
LIBYANG_API_DECL struct lysc_when **lysc_node_when(const struct lysc_node *node);

/**
* @brief Get the target node of a leafref node.
* @brief Get the target node of a leafref node. Function ::lysc_node_lref_targets() should be used instead
* to get all the leafref targets even for a union node.
*
* @param[in] node Leafref node.
* @return Leafref target, NULL on any error.
*/
LIBYANG_API_DECL const struct lysc_node *lysc_node_lref_target(const struct lysc_node *node);

/**
* @brief Get the target node(s) of a leafref node or union node with leafrefs.
*
* @param[in] node Term node to use.
* @param[out] set Set with all the leafref targets, may be empty if the node is a different type or the targets
* are not found.
* @return LY_SUCCESS on success.
* @return LY_ERR value on error.
*/
LIBYANG_API_DECL LY_ERR lysc_node_lref_targets(const struct lysc_node *node, struct ly_set **set);

/**
* @brief Get all the leafref (or union with leafrefs) nodes that target a specific node.
*
* @param[in] ctx Context to use, may not be set if @p node is.
* @param[in] node Leafref target node to use for matching. If not set, all the leafref nodes are just collected.
* @param[in] match_ancestors If set, @p node is considered a match not only when a leafref targets it directly but
* even when an ancestor (parent) node of @p node is a target of the leafref.
* @param[out] set Set of matching leafref nodes.
* @return LY_SUCCESS on success.
* @return LY_ERR value on error.
*/
LIBYANG_API_DECL LY_ERR lysc_node_lref_backlinks(const struct ly_ctx *ctx, const struct lysc_node *node,
ly_bool match_ancestors, struct ly_set **set);

/**
* @brief Callback to be called for every schema node in a DFS traversal.
*
Expand Down
179 changes: 170 additions & 9 deletions src/tree_schema_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -1804,21 +1804,24 @@ lysc_node_when(const struct lysc_node *node)
}
}

LIBYANG_API_DEF const struct lysc_node *
lysc_node_lref_target(const struct lysc_node *node)
/**
* @brief Get the target node of a leafref.
*
* @param[in] node Context node for the leafref.
* @param[in] type Leafref type to resolve.
* @return Target schema node;
* @return NULL if the tearget is not found.
*/
static const struct lysc_node *
lysc_type_lref_target(const struct lysc_node *node, const struct lysc_type *type)
{
struct lysc_type_leafref *lref;
struct ly_path *p;
const struct lysc_node *target;

if (!node || !(node->nodetype & LYD_NODE_TERM)) {
return NULL;
}
assert(type->basetype == LY_TYPE_LEAFREF);

lref = (struct lysc_type_leafref *)((struct lysc_node_leaf *)node)->type;
if (lref->basetype != LY_TYPE_LEAFREF) {
return NULL;
}
lref = (struct lysc_type_leafref *)type;

/* compile the path */
if (ly_path_compile_leafref(node->module->ctx, node, NULL, lref->path,
Expand All @@ -1834,6 +1837,164 @@ lysc_node_lref_target(const struct lysc_node *node)
return target;
}

LIBYANG_API_DEF const struct lysc_node *
lysc_node_lref_target(const struct lysc_node *node)
{
if (!node || !(node->nodetype & LYD_NODE_TERM) || (((struct lysc_node_leaf *)node)->type->basetype != LY_TYPE_LEAFREF)) {
return NULL;
}

return lysc_type_lref_target(node, ((struct lysc_node_leaf *)node)->type);
}

LIBYANG_API_DEF LY_ERR
lysc_node_lref_targets(const struct lysc_node *node, struct ly_set **set)
{
LY_ERR rc = LY_SUCCESS;
struct lysc_type *type;
struct lysc_type_union *type_un;
const struct lysc_node *target;
LY_ARRAY_COUNT_TYPE u;

LY_CHECK_ARG_RET(NULL, node, (node->nodetype & LYD_NODE_TERM), LY_EINVAL);

/* allocate return set */
LY_CHECK_RET(ly_set_new(set));

type = ((struct lysc_node_leaf *)node)->type;
if (type->basetype == LY_TYPE_UNION) {
/* union with possible leafrefs */
type_un = (struct lysc_type_union *)type;

LY_ARRAY_FOR(type_un->types, u) {
if (type_un->types[u]->basetype != LY_TYPE_LEAFREF) {
continue;
}

target = lysc_type_lref_target(node, type_un->types[u]);
if (target) {
LY_CHECK_GOTO(rc = ly_set_add(*set, target, 1, NULL), cleanup);
}
}
} else if (type->basetype == LY_TYPE_LEAFREF) {
/* leafref */
target = lysc_type_lref_target(node, type);
if (target) {
LY_CHECK_GOTO(rc = ly_set_add(*set, target, 1, NULL), cleanup);
}
}

cleanup:
if (rc) {
ly_set_free(*set, NULL);
*set = NULL;
}
return rc;
}

struct lysc_node_lref_backlings_arg {
const struct lysc_node *node;
ly_bool match_ancestors;
struct ly_set *set;
};

static LY_ERR
lysc_node_lref_backlinks_clb(struct lysc_node *node, void *data, ly_bool *dfs_continue)
{
LY_ERR rc = LY_SUCCESS;
struct lysc_node_lref_backlings_arg *arg = data;
struct ly_set *set = NULL;
const struct lysc_node *par;
uint32_t i;

(void)dfs_continue;

if (!(node->nodetype & LYD_NODE_TERM)) {
/* skip */
goto cleanup;
}

/* get all the leafref targets */
LY_CHECK_GOTO(rc = lysc_node_lref_targets(node, &set), cleanup);

/* ignore node if has no leafref targets */
if (!set->count) {
goto cleanup;
}

/* if just collecting leafrefs, we are done */
if (!arg->node) {
rc = ly_set_add(arg->set, node, 1, NULL);
goto cleanup;
}

/* check that the node (or the ancestor of) is the target of this leafref */
for (i = 0; i < set->count; ++i) {
for (par = set->snodes[i]; par; par = par->parent) {
if (par == arg->node) {
/* match */
break;
}

if (!arg->match_ancestors) {
/* not a match */
par = NULL;
break;
}
}

if (par) {
/* add into the set, matches */
LY_CHECK_GOTO(rc = ly_set_add(arg->set, node, 1, NULL), cleanup);
break;
}
}

cleanup:
ly_set_free(set, NULL);
return rc;
}

LIBYANG_API_DEF LY_ERR
lysc_node_lref_backlinks(const struct ly_ctx *ctx, const struct lysc_node *node, ly_bool match_ancestors,
struct ly_set **set)
{
LY_ERR rc = LY_SUCCESS;
struct lysc_node_lref_backlings_arg arg = {0};
uint32_t idx = 0;
const struct lys_module *mod;

LY_CHECK_ARG_RET(NULL, ctx || node, set, LY_EINVAL);

if (!ctx) {
ctx = node->module->ctx;
}

/* allocate return set */
LY_CHECK_RET(ly_set_new(set));

/* prepare the arg */
arg.node = node;
arg.match_ancestors = match_ancestors;
arg.set = *set;

/* iterate across all loaded modules */
while ((mod = ly_ctx_get_module_iter(ctx, &idx))) {
if (!mod->compiled) {
continue;
}

LY_CHECK_GOTO(rc = lysc_module_dfs_full(mod, lysc_node_lref_backlinks_clb, &arg), cleanup);
}

cleanup:
if (rc) {
ly_set_free(*set, NULL);
*set = NULL;
}
return rc;
}

enum ly_stmt
lysp_match_kw(struct ly_in *in, uint64_t *indent)
{
Expand Down
Loading