Add support for identityref data type
Change-Id: I369150b83d86a4c22fadf383ee011eee619a4c15
diff --git a/src/ast_commands.cpp b/src/ast_commands.cpp
index 55b6ccd..a4a9e08 100644
--- a/src/ast_commands.cpp
+++ b/src/ast_commands.cpp
@@ -14,6 +14,19 @@
{
}
+identityRef_::identityRef_() = default;
+
+identityRef_::identityRef_(const std::string& value)
+ : m_value(value)
+{
+}
+
+identityRef_::identityRef_(const std::string& module, const std::string& value)
+ : m_prefix(module_{module})
+ , m_value(value)
+{
+}
+
binary_::binary_() = default;
binary_::binary_(const std::string& value)
@@ -36,6 +49,11 @@
return this->m_path == b.m_path && this->m_options == b.m_options;
}
+bool identityRef_::operator==(const identityRef_& b) const
+{
+ return this->m_prefix == b.m_prefix && this->m_value == b.m_value;
+}
+
bool binary_::operator==(const binary_& b) const
{
return this->m_value == b.m_value;
diff --git a/src/ast_commands.hpp b/src/ast_commands.hpp
index 5015787..f4cd0a9 100644
--- a/src/ast_commands.hpp
+++ b/src/ast_commands.hpp
@@ -198,6 +198,7 @@
BOOST_FUSION_ADAPT_STRUCT(set_, m_path, m_data)
BOOST_FUSION_ADAPT_STRUCT(enum_, m_value)
BOOST_FUSION_ADAPT_STRUCT(binary_, m_value)
+BOOST_FUSION_ADAPT_STRUCT(identityRef_, m_prefix, m_value)
BOOST_FUSION_ADAPT_STRUCT(commit_)
BOOST_FUSION_ADAPT_STRUCT(help_, m_cmd)
BOOST_FUSION_ADAPT_STRUCT(discard_)
diff --git a/src/ast_handlers.hpp b/src/ast_handlers.hpp
index d365641..fede04c 100644
--- a/src/ast_handlers.hpp
+++ b/src/ast_handlers.hpp
@@ -360,6 +360,22 @@
}
};
+// This handler only checks if the module exists
+// It doesn't set any ParserContext flags (except the error message)
+struct data_module_prefix_class {
+ template <typename T, typename Iterator, typename Context>
+ void on_success(Iterator const&, Iterator const&, T& ast, Context const& context)
+ {
+ auto& parserContext = x3::get<parser_context_tag>(context);
+ const auto& schema = parserContext.m_schema;
+
+ if (!schema.isModule(parserContext.m_curPath, ast.m_name)) {
+ parserContext.m_errorMsg = "Invalid module name.";
+ _pass(context) = false;
+ }
+ }
+};
+
struct leaf_data_class {
template <typename Iterator, typename Exception, typename Context>
x3::error_handler_result on_error(Iterator&, Iterator const&, Exception const&, Context const& context)
@@ -369,10 +385,10 @@
if (parserContext.m_errorMsg.empty()) {
leaf_ leaf = boost::get<leaf_>(parserContext.m_curPath.m_nodes.back().m_suffix);
schemaPath_ location = pathWithoutLastNode(parserContext.m_curPath);
- if (location.m_nodes.empty()) {
- parserContext.m_curModule = parserContext.m_curPath.m_nodes.back().m_prefix->m_name;
- }
- parserContext.m_errorMsg = "Expected " + leafDataTypeToString(schema.leafType(location, {parserContext.m_curModule, leaf.m_name})) + " here:";
+ boost::optional<std::string> module;
+ if (parserContext.m_curPath.m_nodes.back().m_prefix)
+ module = parserContext.m_curPath.m_nodes.back().m_prefix.value().m_name;
+ parserContext.m_errorMsg = "Expected " + leafDataTypeToString(schema.leafType(location, {module, leaf.m_name})) + " here:";
return x3::error_handler_result::fail;
}
return x3::error_handler_result::rethrow;
@@ -415,6 +431,9 @@
void on_success(Iterator const& start, Iterator const& end, T& ast, Context const& context)
{
leaf_data_base_class::on_success(start, end, ast, context);
+ // Base class on_success cannot return for us, so we check if it failed the parser.
+ if (_pass(context) == false)
+ return;
auto& parserContext = x3::get<parser_context_tag>(context);
auto& schema = parserContext.m_schema;
boost::optional<std::string> module;
@@ -474,6 +493,43 @@
}
};
+struct leaf_data_identityRef_data_class;
+
+struct leaf_data_identityRef_class : leaf_data_base_class {
+ leaf_data_identityRef_class()
+ : leaf_data_base_class(yang::LeafDataTypes::IdentityRef)
+ {
+ }
+
+ template <typename T, typename Iterator, typename Context>
+ void on_success(Iterator const& start, Iterator const& end, T& ast, Context const& context)
+ {
+ // FIXME: can I reuse leaf_data_enum_class somehow..?
+ leaf_data_base_class::on_success(start, end, ast, context);
+ // Base class on_success cannot return for us, so we check if it failed the parser.
+ if (_pass(context) == false)
+ return;
+ auto& parserContext = x3::get<parser_context_tag>(context);
+ auto& schema = parserContext.m_schema;
+ boost::optional<std::string> module;
+ if (parserContext.m_curPath.m_nodes.back().m_prefix)
+ module = parserContext.m_curPath.m_nodes.back().m_prefix.value().m_name;
+
+ leaf_ leaf = boost::get<leaf_>(parserContext.m_curPath.m_nodes.back().m_suffix);
+ schemaPath_ location = pathWithoutLastNode(parserContext.m_curPath);
+
+ ModuleValuePair pair;
+ if (ast.m_prefix) {
+ pair.first = ast.m_prefix.get().m_name;
+ }
+ pair.second = ast.m_value;
+
+ if (!schema.leafIdentityIsValid(location, {module, leaf.m_name}, pair)) {
+ _pass(context) = false;
+ }
+ }
+};
+
struct set_class {
template <typename Iterator, typename Exception, typename Context>
x3::error_handler_result on_error(Iterator&, Iterator const&, Exception const& x, Context const& context)
@@ -616,6 +672,32 @@
leaf_ leaf = boost::get<leaf_>(parserContext.m_curPath.m_nodes.back().m_suffix);
schemaPath_ location = pathWithoutLastNode(parserContext.m_curPath);
- parserContext.m_suggestions = schema.enumValues(location, {module, leaf.m_name});
+ // Only generate completions if the type is enum so that we don't
+ // overwrite some other completions.
+ if (schema.leafType(location, {module, leaf.m_name}) == yang::LeafDataTypes::Enum)
+ parserContext.m_suggestions = schema.enumValues(location, {module, leaf.m_name});
+ }
+};
+
+// FIXME: can I reuse createEnumSuggestions?
+struct createIdentitySuggestions_class {
+ template <typename T, typename Iterator, typename Context>
+ void on_success(Iterator const& begin, Iterator const&, T&, Context const& context)
+ {
+ auto& parserContext = x3::get<parser_context_tag>(context);
+ parserContext.m_completionIterator = begin;
+ const Schema& schema = parserContext.m_schema;
+
+ boost::optional<std::string> module;
+ if (parserContext.m_curPath.m_nodes.back().m_prefix)
+ module = parserContext.m_curPath.m_nodes.back().m_prefix.value().m_name;
+
+ leaf_ leaf = boost::get<leaf_>(parserContext.m_curPath.m_nodes.back().m_suffix);
+ schemaPath_ location = pathWithoutLastNode(parserContext.m_curPath);
+
+ // Only generate completions if the type is identityref so that we
+ // don't overwrite some other completions.
+ if (schema.leafType(location, {module, leaf.m_name}) == yang::LeafDataTypes::IdentityRef)
+ parserContext.m_suggestions = schema.validIdentities(location, {module, leaf.m_name}, Prefixes::WhenNeeded);
}
};
diff --git a/src/ast_path.hpp b/src/ast_path.hpp
index 557b01f..dcd13ff 100644
--- a/src/ast_path.hpp
+++ b/src/ast_path.hpp
@@ -18,6 +18,8 @@
#include <map>
#include <vector>
+#include "ast_values.hpp"
+
struct nodeup_ {
bool operator==(const nodeup_&) const
{
@@ -62,11 +64,6 @@
std::string m_name;
};
-struct module_ {
- bool operator==(const module_& b) const;
- std::string m_name;
-};
-
struct schemaNode_ {
boost::optional<module_> m_prefix;
boost::variant<container_, list_, nodeup_, leaf_> m_suffix;
diff --git a/src/ast_values.hpp b/src/ast_values.hpp
index b9053c0..350fc8d 100644
--- a/src/ast_values.hpp
+++ b/src/ast_values.hpp
@@ -7,6 +7,7 @@
*/
#pragma once
+#include <boost/optional.hpp>
#include <boost/variant.hpp>
struct enum_ {
@@ -23,8 +24,23 @@
std::string m_value;
};
+struct module_ {
+ bool operator==(const module_& b) const;
+ std::string m_name;
+};
+
+struct identityRef_ {
+ identityRef_();
+ identityRef_(const std::string& module, const std::string& value);
+ identityRef_(const std::string& value);
+ bool operator==(const identityRef_& b) const;
+ boost::optional<module_> m_prefix;
+ std::string m_value;
+};
+
using leaf_data_ = boost::variant<enum_,
binary_,
+ identityRef_,
double,
bool,
int32_t,
diff --git a/src/grammars.hpp b/src/grammars.hpp
index a2e68fc..9ba81c6 100644
--- a/src/grammars.hpp
+++ b/src/grammars.hpp
@@ -46,6 +46,8 @@
x3::rule<leaf_data_string_class, std::string> const leaf_data_string = "leaf_data_string";
x3::rule<leaf_data_binary_data_class, std::string> const leaf_data_binary_data = "leaf_data_binary_data";
x3::rule<leaf_data_binary_class, binary_> const leaf_data_binary = "leaf_data_binary";
+x3::rule<leaf_data_identityRef_data_class, identityRef_> const leaf_data_identityRef_data = "leaf_data_identityRef_data";
+x3::rule<leaf_data_identityRef_class, identityRef_> const leaf_data_identityRef = "leaf_data_identityRef";
x3::rule<discard_class, discard_> const discard = "discard";
x3::rule<ls_class, ls_> const ls = "ls";
@@ -65,6 +67,7 @@
x3::rule<createCommandSuggestions_class, x3::unused_type> const createCommandSuggestions = "createCommandSuggestions";
x3::rule<completing_class, x3::unused_type> const completing = "completing";
x3::rule<createEnumSuggestions_class, x3::unused_type> const createEnumSuggestions = "createEnumSuggestions";
+x3::rule<createIdentitySuggestions_class, x3::unused_type> const createIdentitySuggestions = "createIdentitySuggestions";
#if __clang__
#pragma GCC diagnostic push
@@ -219,6 +222,15 @@
auto const leaf_data_binary_def =
leaf_data_binary_data;
+auto const leaf_data_identityRef_data_def =
+ -module >> node_identifier;
+
+auto const createIdentitySuggestions_def =
+ x3::eps;
+
+auto const leaf_data_identityRef_def =
+ createIdentitySuggestions >> leaf_data_identityRef_data;
+
auto const leaf_data_def =
x3::expect[
leaf_data_enum |
@@ -227,6 +239,7 @@
leaf_data_int |
leaf_data_uint |
leaf_data_binary |
+ leaf_data_identityRef |
leaf_data_string];
struct ls_options_table : x3::symbols<LsOption> {
@@ -320,6 +333,8 @@
BOOST_SPIRIT_DEFINE(leaf_data_string)
BOOST_SPIRIT_DEFINE(leaf_data_binary_data)
BOOST_SPIRIT_DEFINE(leaf_data_binary)
+BOOST_SPIRIT_DEFINE(leaf_data_identityRef_data)
+BOOST_SPIRIT_DEFINE(leaf_data_identityRef)
BOOST_SPIRIT_DEFINE(initializePath)
BOOST_SPIRIT_DEFINE(set)
BOOST_SPIRIT_DEFINE(commit)
@@ -337,3 +352,4 @@
BOOST_SPIRIT_DEFINE(createCommandSuggestions)
BOOST_SPIRIT_DEFINE(completing)
BOOST_SPIRIT_DEFINE(createEnumSuggestions)
+BOOST_SPIRIT_DEFINE(createIdentitySuggestions)
diff --git a/src/interpreter.cpp b/src/interpreter.cpp
index 44177b6..e08eab2 100644
--- a/src/interpreter.cpp
+++ b/src/interpreter.cpp
@@ -21,6 +21,11 @@
return data.m_value;
}
+ std::string operator()(const identityRef_& data) const
+ {
+ return data.m_value;
+ }
+
template <typename T>
std::string operator()(const T& data) const
{
@@ -42,7 +47,17 @@
void Interpreter::operator()(const set_& set) const
{
- m_datastore.setLeaf(absolutePathFromCommand(set), set.m_data);
+ auto data = set.m_data;
+
+ // If the user didn't supply a module prefix for identityref, we need to add it ourselves
+ if (data.type() == typeid(identityRef_)) {
+ auto identityRef = boost::get<identityRef_>(data);
+ if (!identityRef.m_prefix) {
+ identityRef.m_prefix = set.m_path.m_nodes.front().m_prefix.value();
+ data = identityRef;
+ }
+ }
+ m_datastore.setLeaf(absolutePathFromCommand(set), data);
}
void Interpreter::operator()(const get_& get) const
diff --git a/src/schema.hpp b/src/schema.hpp
index aaeb846..6a2240d 100644
--- a/src/schema.hpp
+++ b/src/schema.hpp
@@ -15,6 +15,8 @@
#include <unordered_map>
#include "ast_path.hpp"
+using ModuleValuePair = std::pair<boost::optional<std::string>, std::string>;
+
namespace yang {
enum class ContainerTraits {
Presence,
@@ -29,6 +31,7 @@
Uint,
Enum,
Binary,
+ IdentityRef,
};
struct container {
@@ -37,9 +40,11 @@
struct list {
std::set<std::string> m_keys;
};
+
struct leaf {
yang::LeafDataTypes m_type;
std::set<std::string> m_enumValues;
+ ModuleValuePair m_identBase;
};
struct module {
@@ -51,6 +56,11 @@
Recursive
};
+enum class Prefixes {
+ Always,
+ WhenNeeded
+};
+
using NodeType = boost::variant<yang::container, yang::list, yang::leaf, yang::module>;
@@ -76,10 +86,12 @@
virtual bool isList(const schemaPath_& location, const ModuleNodePair& node) const = 0;
virtual bool isPresenceContainer(const schemaPath_& location, const ModuleNodePair& node) const = 0;
virtual bool leafEnumHasValue(const schemaPath_& location, const ModuleNodePair& node, const std::string& value) const = 0;
+ virtual bool leafIdentityIsValid(const schemaPath_& location, const ModuleNodePair& node, const ModuleValuePair& value) const = 0;
virtual bool listHasKey(const schemaPath_& location, const ModuleNodePair& node, const std::string& key) const = 0;
virtual bool nodeExists(const std::string& location, const std::string& node) const = 0;
virtual const std::set<std::string> listKeys(const schemaPath_& location, const ModuleNodePair& node) const = 0;
virtual yang::LeafDataTypes leafType(const schemaPath_& location, const ModuleNodePair& node) const = 0;
+ virtual const std::set<std::string> validIdentities(const schemaPath_& location, const ModuleNodePair& node, const Prefixes prefixes) const = 0;
virtual const std::set<std::string> enumValues(const schemaPath_& location, const ModuleNodePair& node) const = 0;
virtual std::set<std::string> childNodes(const schemaPath_& path, const Recursion recursion) const = 0;
diff --git a/src/static_schema.cpp b/src/static_schema.cpp
index d80ac7b..a3649ae 100644
--- a/src/static_schema.cpp
+++ b/src/static_schema.cpp
@@ -16,7 +16,6 @@
m_nodes.emplace("", std::unordered_map<std::string, NodeType>());
}
-
const std::unordered_map<std::string, NodeType>& StaticSchema::children(const std::string& name) const
{
return m_nodes.at(name);
@@ -105,12 +104,24 @@
void StaticSchema::addLeaf(const std::string& location, const std::string& name, const yang::LeafDataTypes& type)
{
- m_nodes.at(location).emplace(name, yang::leaf{type, {}});
+ m_nodes.at(location).emplace(name, yang::leaf{type, {}, {}});
}
void StaticSchema::addLeafEnum(const std::string& location, const std::string& name, std::set<std::string> enumValues)
{
- m_nodes.at(location).emplace(name, yang::leaf{yang::LeafDataTypes::Enum, enumValues});
+ yang::leaf toAdd;
+ toAdd.m_type = yang::LeafDataTypes::Enum;
+ toAdd.m_enumValues = enumValues;
+ m_nodes.at(location).emplace(name, toAdd);
+}
+
+void StaticSchema::addLeafIdentityRef(const std::string& location, const std::string& name, const ModuleValuePair& base)
+{
+ assert(base.first); // base identity cannot have an empty module
+ yang::leaf toAdd;
+ toAdd.m_type = yang::LeafDataTypes::IdentityRef;
+ toAdd.m_identBase = base;
+ m_nodes.at(location).emplace(name, toAdd);
}
void StaticSchema::addModule(const std::string& name)
@@ -118,6 +129,13 @@
m_modules.emplace(name);
}
+void StaticSchema::addIdentity(const std::optional<ModuleValuePair>& base, const ModuleValuePair& name)
+{
+ if (base)
+ m_identities.at(base.value()).emplace(name);
+
+ m_identities.emplace(name, std::set<ModuleValuePair>());
+}
bool StaticSchema::leafEnumHasValue(const schemaPath_& location, const ModuleNodePair& node, const std::string& value) const
{
@@ -125,6 +143,50 @@
return enums.find(value) != enums.end();
}
+void StaticSchema::getIdentSet(const ModuleValuePair& ident, std::set<ModuleValuePair>& res) const
+{
+ res.insert(ident);
+ auto derivedIdentities = m_identities.at(ident);
+ for (auto it : derivedIdentities) {
+ getIdentSet(it, res);
+ }
+}
+
+const std::set<std::string> StaticSchema::validIdentities(const schemaPath_& location, const ModuleNodePair& node, const Prefixes prefixes) const
+{
+ std::string locationString = pathToAbsoluteSchemaString(location);
+ assert(isLeaf(location, node));
+
+ const auto& child = children(locationString).at(fullNodeName(location, node));
+ const auto& leaf = boost::get<yang::leaf>(child);
+
+ std::set<ModuleValuePair> identSet;
+ getIdentSet(leaf.m_identBase, identSet);
+
+ std::set<std::string> res;
+ std::transform(identSet.begin(), identSet.end(), std::inserter(res, res.end()), [location, node, prefixes](const auto& it) {
+ auto topLevelModule = location.m_nodes.empty() ? node.first.get() : location.m_nodes.front().m_prefix.get().m_name;
+ std::string stringIdent;
+ if (prefixes == Prefixes::Always || (it.first && it.first.value() != topLevelModule)) {
+ stringIdent += it.first ? it.first.value() : topLevelModule;
+ stringIdent += ":";
+ }
+ stringIdent += it.second;
+ return stringIdent;
+ });
+
+ return res;
+}
+
+bool StaticSchema::leafIdentityIsValid(const schemaPath_& location, const ModuleNodePair& node, const ModuleValuePair& value) const
+{
+ auto identities = validIdentities(location, node, Prefixes::Always);
+
+ auto topLevelModule = location.m_nodes.empty() ? node.first.get() : location.m_nodes.front().m_prefix.get().m_name;
+ auto identModule = value.first ? value.first.value() : topLevelModule;
+ return std::any_of(identities.begin(), identities.end(), [identModule, value](const auto& x) { return x == identModule + ":" + value.second; });
+}
+
bool StaticSchema::isLeaf(const schemaPath_& location, const ModuleNodePair& node) const
{
std::string locationString = pathToAbsoluteSchemaString(location);
diff --git a/src/static_schema.hpp b/src/static_schema.hpp
index d07852e..3bf60f0 100644
--- a/src/static_schema.hpp
+++ b/src/static_schema.hpp
@@ -29,22 +29,28 @@
bool isList(const schemaPath_& location, const ModuleNodePair& node) const override;
bool isPresenceContainer(const schemaPath_& location, const ModuleNodePair& node) const override;
bool leafEnumHasValue(const schemaPath_& location, const ModuleNodePair& node, const std::string& value) const override;
+ bool leafIdentityIsValid(const schemaPath_& location, const ModuleNodePair& node, const ModuleValuePair& value) const override;
bool listHasKey(const schemaPath_& location, const ModuleNodePair& node, const std::string& key) const override;
bool nodeExists(const std::string& location, const std::string& node) const override;
const std::set<std::string> listKeys(const schemaPath_& location, const ModuleNodePair& node) const override;
yang::LeafDataTypes leafType(const schemaPath_& location, const ModuleNodePair& node) const override;
const std::set<std::string> enumValues(const schemaPath_& location, const ModuleNodePair& node) const override;
+ const std::set<std::string> validIdentities(const schemaPath_& location, const ModuleNodePair& node, const Prefixes prefixes) const override;
std::set<std::string> childNodes(const schemaPath_& path, const Recursion) const override;
void addContainer(const std::string& location, const std::string& name, yang::ContainerTraits isPresence = yang::ContainerTraits::None);
void addLeaf(const std::string& location, const std::string& name, const yang::LeafDataTypes& type);
void addLeafEnum(const std::string& location, const std::string& name, std::set<std::string> enumValues);
+ void addLeafIdentityRef(const std::string& location, const std::string& name, const ModuleValuePair& base);
void addList(const std::string& location, const std::string& name, const std::set<std::string>& keys);
void addModule(const std::string& name);
+ void addIdentity(const std::optional<ModuleValuePair>& base, const ModuleValuePair& name);
private:
const std::unordered_map<std::string, NodeType>& children(const std::string& name) const;
+ void getIdentSet(const ModuleValuePair& ident, std::set<ModuleValuePair>& res) const;
std::unordered_map<std::string, std::unordered_map<std::string, NodeType>> m_nodes;
std::set<std::string> m_modules;
+ std::map<ModuleValuePair, std::set<ModuleValuePair>> m_identities;
};
diff --git a/src/sysrepo_access.cpp b/src/sysrepo_access.cpp
index d514203..4e1ab4b 100644
--- a/src/sysrepo_access.cpp
+++ b/src/sysrepo_access.cpp
@@ -48,6 +48,12 @@
return std::make_shared<sysrepo::Val>(value.m_value.c_str(), SR_BINARY_T);
}
+ sysrepo::S_Val operator()(const identityRef_& value) const
+ {
+ auto res = value.m_prefix.value().m_name + ":" + value.m_value;
+ return std::make_shared<sysrepo::Val>(res.c_str(), SR_IDENTITYREF_T);
+ }
+
sysrepo::S_Val operator()(const std::string& value) const
{
return std::make_shared<sysrepo::Val>(value.c_str());
diff --git a/src/utils.cpp b/src/utils.cpp
index 595dc7a..421fc3e 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -48,8 +48,10 @@
return "an unsigned integer";
case yang::LeafDataTypes::Enum:
return "an enum";
+ case yang::LeafDataTypes::IdentityRef:
+ return "an identity";
default:
- return "";
+ throw std::runtime_error("leafDataTypeToString: unsupported leaf data type");
}
}
diff --git a/src/yang_schema.cpp b/src/yang_schema.cpp
index f6e0eea..757c9f4 100644
--- a/src/yang_schema.cpp
+++ b/src/yang_schema.cpp
@@ -138,6 +138,46 @@
return enumSet;
}
+const std::set<std::string> YangSchema::validIdentities(const schemaPath_& location, const ModuleNodePair& node, const Prefixes prefixes) const
+{
+ if (!isLeaf(location, node) || leafType(location, node) != yang::LeafDataTypes::IdentityRef)
+ return {};
+
+ std::set<std::string> identSet;
+
+ auto topLevelModule = location.m_nodes.empty() ? node.first.get() : location.m_nodes.front().m_prefix.get().m_name;
+ auto insertToSet = [&identSet, prefixes, topLevelModule](auto module, auto name) {
+ std::string stringIdent;
+ if (prefixes == Prefixes::Always || topLevelModule != module) {
+ stringIdent += module;
+ stringIdent += ":";
+ }
+ stringIdent += name;
+ identSet.emplace(stringIdent);
+ };
+
+ auto leaf = std::make_shared<libyang::Schema_Node_Leaf>(getSchemaNode(location, node));
+ auto info = leaf->type()->info();
+ for (auto base : info->ident()->ref()) { // Iterate over all bases
+ insertToSet(base->module()->name(), base->name());
+ // Iterate over derived identities (this is recursive!)
+ for (auto derived : base->der()->schema()) {
+ insertToSet(derived->module()->name(), derived->name());
+ }
+ }
+
+ return identSet;
+}
+
+bool YangSchema::leafIdentityIsValid(const schemaPath_& location, const ModuleNodePair& node, const ModuleValuePair& value) const
+{
+ auto identities = validIdentities(location, node, Prefixes::Always);
+
+ auto topLevelModule = location.m_nodes.empty() ? node.first.get() : location.m_nodes.front().m_prefix.get().m_name;
+ auto identModule = value.first ? value.first.value() : topLevelModule;
+ return std::any_of(identities.begin(), identities.end(), [identModule, value](const auto& x) { return x == identModule + ":" + value.second; });
+}
+
bool YangSchema::listHasKey(const schemaPath_& location, const ModuleNodePair& node, const std::string& key) const
{
if (!isList(location, node))
@@ -206,6 +246,8 @@
return yang::LeafDataTypes::Enum;
case LY_TYPE_BINARY:
return yang::LeafDataTypes::Binary;
+ case LY_TYPE_IDENT:
+ return yang::LeafDataTypes::IdentityRef;
default:
throw UnsupportedYangTypeException("the type of "s + fullNodeName(location, node) + " is not supported");
}
diff --git a/src/yang_schema.hpp b/src/yang_schema.hpp
index e56c853..64e3ac5 100644
--- a/src/yang_schema.hpp
+++ b/src/yang_schema.hpp
@@ -35,10 +35,12 @@
bool isList(const schemaPath_& location, const ModuleNodePair& node) const override;
bool isPresenceContainer(const schemaPath_& location, const ModuleNodePair& node) const override;
bool leafEnumHasValue(const schemaPath_& location, const ModuleNodePair& node, const std::string& value) const override;
+ bool leafIdentityIsValid(const schemaPath_& location, const ModuleNodePair& node, const ModuleValuePair& value) const override;
bool listHasKey(const schemaPath_& location, const ModuleNodePair& node, const std::string& key) const override;
bool nodeExists(const std::string& location, const std::string& node) const override;
const std::set<std::string> listKeys(const schemaPath_& location, const ModuleNodePair& node) const override;
yang::LeafDataTypes leafType(const schemaPath_& location, const ModuleNodePair& node) const override;
+ const std::set<std::string> validIdentities(const schemaPath_& location, const ModuleNodePair& node, const Prefixes prefixes) const override;
const std::set<std::string> enumValues(const schemaPath_& location, const ModuleNodePair& node) const override;
std::set<std::string> childNodes(const schemaPath_& path, const Recursion recursion) const override;
diff --git a/tests/leaf_editing.cpp b/tests/leaf_editing.cpp
index f18af1c..925a238 100644
--- a/tests/leaf_editing.cpp
+++ b/tests/leaf_editing.cpp
@@ -15,6 +15,7 @@
{
auto schema = std::make_shared<StaticSchema>();
schema->addModule("mod");
+ schema->addModule("pizza-module");
schema->addContainer("", "mod:contA");
schema->addLeaf("", "mod:leafString", yang::LeafDataTypes::String);
schema->addLeaf("", "mod:leafDecimal", yang::LeafDataTypes::Decimal);
@@ -22,6 +23,14 @@
schema->addLeaf("", "mod:leafInt", yang::LeafDataTypes::Int);
schema->addLeaf("", "mod:leafUint", yang::LeafDataTypes::Uint);
schema->addLeaf("", "mod:leafBinary", yang::LeafDataTypes::Binary);
+ schema->addIdentity(std::nullopt, ModuleValuePair{"mod", "food"});
+ schema->addIdentity(std::nullopt, ModuleValuePair{"mod", "vehicle"});
+ schema->addIdentity(ModuleValuePair{"mod", "food"}, ModuleValuePair{"mod", "pizza"});
+ schema->addIdentity(ModuleValuePair{"mod", "food"}, ModuleValuePair{"mod", "spaghetti"});
+ schema->addIdentity(ModuleValuePair{"mod", "pizza"}, ModuleValuePair{"pizza-module", "hawaii"});
+ schema->addLeafIdentityRef("", "mod:foodIdentRef", ModuleValuePair{"mod", "food"});
+ schema->addLeafIdentityRef("", "mod:pizzaIdentRef", ModuleValuePair{"mod", "pizza"});
+ schema->addLeafIdentityRef("mod:contA", "mod:identInCont", ModuleValuePair{"mod", "pizza"});
schema->addLeafEnum("", "mod:leafEnum", {"lol", "data", "coze"});
schema->addLeaf("mod:contA", "mod:leafInCont", yang::LeafDataTypes::String);
schema->addList("", "mod:list", {"number"});
@@ -117,6 +126,82 @@
}
expected.m_path.m_nodes.push_back(dataNode_{module_{"mod"}, leaf_("leafBinary")});
}
+
+ SECTION("identityRef")
+ {
+ SECTION("foodIdentRef")
+ {
+ input = "set mod:foodIdentRef ";
+ expected.m_path.m_nodes.push_back(dataNode_{module_{"mod"}, leaf_("foodIdentRef")});
+
+ SECTION("food")
+ {
+ input += "food";
+ expected.m_data = identityRef_("food");
+ }
+ SECTION("mod:food")
+ {
+ input += "mod:food";
+ expected.m_data = identityRef_("mod", "food");
+ }
+ SECTION("pizza")
+ {
+ input += "pizza";
+ expected.m_data = identityRef_("pizza");
+ }
+ SECTION("mod:pizza")
+ {
+ input += "mod:pizza";
+ expected.m_data = identityRef_("mod", "pizza");
+ }
+ SECTION("pizza-module:hawaii")
+ {
+ input += "pizza-module:hawaii";
+ expected.m_data = identityRef_("pizza-module", "hawaii");
+ }
+ }
+ SECTION("pizzaIdentRef")
+ {
+ input = "set mod:pizzaIdentRef ";
+ expected.m_path.m_nodes.push_back(dataNode_{module_{"mod"}, leaf_("pizzaIdentRef")});
+ SECTION("pizza")
+ {
+ input += "pizza";
+ expected.m_data = identityRef_("pizza");
+ }
+ SECTION("mod:pizza")
+ {
+ input += "mod:pizza";
+ expected.m_data = identityRef_("mod", "pizza");
+ }
+ SECTION("pizza-module:hawaii")
+ {
+ input += "pizza-module:hawaii";
+ expected.m_data = identityRef_("pizza-module", "hawaii");
+ }
+ }
+ SECTION("mod:contA/identInCont")
+ {
+ input = "set mod:contA/identInCont ";
+ expected.m_path.m_nodes.push_back(dataNode_{module_{"mod"}, container_("contA")});
+ expected.m_path.m_nodes.push_back(dataNode_(leaf_("identInCont")));
+ SECTION("pizza")
+ {
+ input += "pizza";
+ expected.m_data = identityRef_("pizza");
+ }
+ SECTION("mod:pizza")
+ {
+ input += "mod:pizza";
+ expected.m_data = identityRef_("mod", "pizza");
+ }
+ SECTION("pizza-module:hawaii")
+ {
+ input += "pizza-module:hawaii";
+ expected.m_data = identityRef_("pizza-module", "hawaii");
+ }
+ }
+ }
}
command_ command = parser.parseCommand(input, errorStream);
@@ -183,6 +268,31 @@
input = "set leafBinary db=ahj";
}
+ SECTION("non-existing identity")
+ {
+ input = "set mod:foodIdentRef identityBLABLA";
+ }
+
+ SECTION("setting identities with wrong bases")
+ {
+ SECTION("set mod:foodIdentRef mod:vehicle")
+ {
+ input = "set mod:foodIdentRef mod:vehicle";
+ }
+ SECTION("set mod:pizzaIdentRef mod:food")
+ {
+ input = "set mod:pizzaIdentRef mod:food";
+ }
+ }
+ SECTION("setting different module identities without prefix")
+ {
+ input = "set mod:pizzaIdentRef hawaii";
+ }
+ SECTION("identity prefix without name")
+ {
+ input = "set mod:contA/identInCont pizza-module:";
+ }
+
REQUIRE_THROWS_AS(parser.parseCommand(input, errorStream), InvalidCommandException);
}
}
diff --git a/tests/yang.cpp b/tests/yang.cpp
index 5846a23..e19f991 100644
--- a/tests/yang.cpp
+++ b/tests/yang.cpp
@@ -18,6 +18,10 @@
prefix "example";
}
+ identity pineapple {
+ base "example:fruit";
+ }
+
augment /example:a {
container augmentedContainer {
}
@@ -36,6 +40,28 @@
namespace "http://example.com/example-sports";
prefix coze;
+ identity drink {
+ }
+
+ identity voda {
+ base "drink";
+ }
+
+ identity food {
+ }
+
+ identity fruit {
+ base "food";
+ }
+
+ identity pizza {
+ base "food";
+ }
+
+ identity hawaii {
+ base "pizza";
+ }
+
container a {
container a2 {
container a3 {
@@ -116,6 +142,25 @@
type enumTypedefRestricted;
}
+ leaf foodIdentLeaf {
+ type identityref {
+ base "food";
+ }
+ }
+
+ leaf pizzaIdentLeaf {
+ type identityref {
+ base "pizza";
+ }
+ }
+
+ leaf foodDrinkIdentLeaf {
+ type identityref {
+ base "food";
+ base "drink";
+ }
+ }
+
list _list {
key number;
@@ -279,6 +324,110 @@
REQUIRE(ys.leafEnumHasValue(path, node, value));
}
+ SECTION("leafIdentityIsValid")
+ {
+ ModuleValuePair value;
+
+ SECTION("foodIdentLeaf")
+ {
+ node.first = "example-schema";
+ node.second = "foodIdentLeaf";
+
+ SECTION("food")
+ {
+ value.second = "food";
+ }
+ SECTION("example-schema:food")
+ {
+ value.first = "example-schema";
+ value.second = "food";
+ }
+ SECTION("pizza")
+ {
+ value.second = "pizza";
+ }
+ SECTION("example-schema:pizza")
+ {
+ value.first = "example-schema";
+ value.second = "pizza";
+ }
+ SECTION("hawaii")
+ {
+ value.second = "hawaii";
+ }
+ SECTION("example-schema:hawaii")
+ {
+ value.first = "example-schema";
+ value.second = "hawaii";
+ }
+ SECTION("fruit")
+ {
+ value.second = "fruit";
+ }
+ SECTION("example-schema:fruit")
+ {
+ value.first = "example-schema";
+ value.second = "fruit";
+ }
+ SECTION("second-schema:pineapple")
+ {
+ value.first = "second-schema";
+ value.second = "pineapple";
+ }
+ }
+
+ SECTION("pizzaIdentLeaf")
+ {
+ node.first = "example-schema";
+ node.second = "pizzaIdentLeaf";
+
+ SECTION("pizza")
+ {
+ value.second = "pizza";
+ }
+ SECTION("example-schema:pizza")
+ {
+ value.first = "example-schema";
+ value.second = "pizza";
+ }
+ SECTION("hawaii")
+ {
+ value.second = "hawaii";
+ }
+ SECTION("example-schema:hawaii")
+ {
+ value.first = "example-schema";
+ value.second = "hawaii";
+ }
+ }
+
+ SECTION("foodDrinkIdentLeaf")
+ {
+ node.first = "example-schema";
+ node.second = "foodDrinkIdentLeaf";
+
+ SECTION("food")
+ {
+ value.second = "food";
+ }
+ SECTION("example-schema:food")
+ {
+ value.first = "example-schema";
+ value.second = "food";
+ }
+ SECTION("drink")
+ {
+ value.second = "drink";
+ }
+ SECTION("example-schema:drink")
+ {
+ value.first = "example-schema";
+ value.second = "drink";
+ }
+ }
+ REQUIRE(ys.leafIdentityIsValid(path, node, value));
+ }
+
SECTION("listHasKey")
{
std::string key;
@@ -381,6 +530,7 @@
"example-schema:leafDecimal", "example-schema:leafBool", "example-schema:leafInt",
"example-schema:leafUint", "example-schema:leafEnum", "example-schema:leafEnumTypedef",
"example-schema:leafEnumTypedefRestricted", "example-schema:leafEnumTypedefRestricted2",
+ "example-schema:foodIdentLeaf", "example-schema:pizzaIdentLeaf", "example-schema:foodDrinkIdentLeaf",
"example-schema:_list", "example-schema:twoKeyList", "second-schema:bla"};
}
@@ -460,5 +610,43 @@
{
REQUIRE(!ys.isModule(path, "notAModule"));
}
+
+ SECTION("leafIdentityIsValid")
+ {
+ ModuleValuePair value;
+ SECTION("pizzaIdentLeaf")
+ {
+ node.first = "example-schema";
+ node.second = "pizzaIdentLeaf";
+
+ SECTION("wrong base ident")
+ {
+ SECTION("food")
+ {
+ value.second = "food";
+ }
+ SECTION("fruit")
+ {
+ value.second = "fruit";
+ }
+ }
+ SECTION("non-existent identity")
+ {
+ value.second = "nonexistent";
+ }
+ SECTION("weird module")
+ {
+ value.first = "ahahaha";
+ value.second = "pizza";
+ }
+ }
+ SECTION("different module identity, but withotu prefix")
+ {
+ node.first = "example-schema";
+ node.second = "foodIdentLeaf";
+ value.second = "pineapple";
+ }
+ REQUIRE_FALSE(ys.leafIdentityIsValid(path, node, value));
+ }
}
}