Merge "Rework path parsing"
diff --git a/src/common_parsers.hpp b/src/common_parsers.hpp
index eac8e61..8da90a6 100644
--- a/src/common_parsers.hpp
+++ b/src/common_parsers.hpp
@@ -34,6 +34,22 @@
auto const space_separator_def =
x3::omit[x3::no_skip[x3::space]];
+template <typename CoerceTo>
+struct as_type {
+ template <typename...> struct Tag{};
+
+ template <typename ParserType>
+ auto operator[](ParserType p) const {
+ return x3::rule<Tag<CoerceTo, ParserType>, CoerceTo> {"as"} = x3::as_parser(p);
+ }
+};
+
+// The `as` parser creates an ad-hoc x3::rule with the attribute specified with `CoerceTo`.
+// Example usage: as<std::string>[someParser]
+// someParser will have its attribute coerced to std::string
+// https://github.com/boostorg/spirit/issues/530#issuecomment-584836532
+template <typename CoerceTo> const as_type<CoerceTo> as{};
+
BOOST_SPIRIT_DEFINE(node_identifier)
BOOST_SPIRIT_DEFINE(module)
BOOST_SPIRIT_DEFINE(module_identifier)
diff --git a/src/leaf_data.hpp b/src/leaf_data.hpp
index 68705d0..5a181e5 100644
--- a/src/leaf_data.hpp
+++ b/src/leaf_data.hpp
@@ -22,8 +22,6 @@
x3::rule<struct leaf_data_class<yang::Binary>, binary_> const leaf_data_binary = "leaf_data_binary";
x3::rule<struct leaf_data_class<yang::Decimal>, double> const leaf_data_decimal = "leaf_data_decimal";
x3::rule<struct leaf_data_class<yang::String>, std::string> const leaf_data_string = "leaf_data_string";
-x3::rule<struct leaf_data_class_binary, std::string> const leaf_data_binary_data = "leaf_data_binary_data";
-x3::rule<struct leaf_data_identityRef_data_class, identityRef_> const leaf_data_identityRef_data = "leaf_data_identityRef_data";
using x3::char_;
@@ -43,22 +41,11 @@
'\'' >> *(char_-'\'') >> '\'' |
'\"' >> *(char_-'\"') >> '\"';
-// This intermediate rule is neccessary for coercing to std::string.
-// TODO: check if I can do the coercing right in the grammar with `as{}` from
-// https://github.com/boostorg/spirit/issues/530#issuecomment-584836532
-// This would shave off some more lines.
-auto const leaf_data_binary_data_def =
- +(x3::alnum | char_('+') | char_('/')) >> -char_('=') >> -char_('=');
-
auto const leaf_data_binary_def =
- leaf_data_binary_data;
+ as<std::string>[+(x3::alnum | char_('+') | char_('/')) >> -char_('=') >> -char_('=')];
-auto const leaf_data_identityRef_data_def =
- -module >> node_identifier;
-
-// TODO: get rid of this and use leaf_data_identityRef_data directly
auto const leaf_data_identityRef_def =
- leaf_data_identityRef_data;
+ -module >> node_identifier;
template <typename It, typename Ctx, typename RCtx, typename Attr>
struct impl_LeafData {
@@ -130,44 +117,29 @@
bool operator()(const yang::Enum& type) const
{
createSetSuggestions(type);
- // leaf_data_enum will advance the iterator if it succeeds, so I have
- // to save the iterator here, to roll it back in case the enum is
- // invalid.
- auto saveIter = first;
- auto pass = leaf_data_enum.parse(first, last, ctx, rctx, attr);
- if (!pass) {
- return false;
- }
- auto isValidEnum = type.m_allowedValues.count(boost::get<enum_>(attr)) != 0;
- if (!isValidEnum) {
- first = saveIter;
- parserContext.m_errorMsg = "leaf data type mismatch: Expected an enum here. Allowed values:";
- for (const auto& it : type.m_allowedValues) {
- parserContext.m_errorMsg += " " + it.m_value;
+ auto checkValidEnum = [this, type] (auto& ctx) {
+ if (type.m_allowedValues.count(boost::get<enum_>(attr)) == 0) {
+ _pass(ctx) = false;
+ parserContext.m_errorMsg = "leaf data type mismatch: Expected an enum here. Allowed values:";
+ for (const auto& it : type.m_allowedValues) {
+ parserContext.m_errorMsg += " " + it.m_value;
+ }
}
- }
- return isValidEnum;
+ };
+ return leaf_data_enum[checkValidEnum].parse(first, last, ctx, rctx, attr);
}
bool operator()(const yang::IdentityRef& type) const
{
createSetSuggestions(type);
- // leaf_data_identityRef will advance the iterator if it succeeds, so I have
- // to save the iterator here, to roll it back in case the enum is
- // invalid.
- auto saveIter = first;
- auto pass = leaf_data_identityRef.parse(first, last, ctx, rctx, attr);
- if (!pass) {
- return false;
- }
- identityRef_ pair{boost::get<identityRef_>(attr)};
- if (!pair.m_prefix) {
- pair.m_prefix = module_{parserContext.currentSchemaPath().m_nodes.front().m_prefix.get().m_name};
- }
- auto isValidIdentity = type.m_allowedValues.count(pair) != 0;
- if (!isValidIdentity) {
- first = saveIter;
- }
- return isValidIdentity;
+ auto checkValidIdentity = [this, type] (auto& ctx) {
+ identityRef_ pair{boost::get<identityRef_>(_attr(ctx))};
+ if (!pair.m_prefix) {
+ pair.m_prefix = module_{parserContext.currentSchemaPath().m_nodes.front().m_prefix.get().m_name};
+ }
+ _pass(ctx) = type.m_allowedValues.count(pair) != 0;
+ };
+
+ return leaf_data_identityRef[checkValidIdentity].parse(first, last, ctx, rctx, attr);
}
bool operator()(const yang::LeafRef& leafRef) const
{
@@ -207,7 +179,5 @@
BOOST_SPIRIT_DEFINE(leaf_data_enum)
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)
diff --git a/src/libyang_utils.cpp b/src/libyang_utils.cpp
index 9051e16..ddb3630 100644
--- a/src/libyang_utils.cpp
+++ b/src/libyang_utils.cpp
@@ -27,8 +27,10 @@
return std::string(value->string());
case LY_TYPE_ENUM:
return enum_{std::string(value->enm()->name())};
+ case LY_TYPE_IDENT:
+ return identityRef_{value->ident()->module()->name(), value->ident()->name()};
case LY_TYPE_BINARY:
- return std::string{value->binary()};
+ return binary_{value->binary()};
case LY_TYPE_DEC64:
{
auto v = value->dec64();
diff --git a/src/schema.hpp b/src/schema.hpp
index f11265f..0a9b2ff 100644
--- a/src/schema.hpp
+++ b/src/schema.hpp
@@ -15,8 +15,6 @@
#include "ast_path.hpp"
#include "leaf_data_type.hpp"
-using ModuleValuePair = std::pair<boost::optional<std::string>, std::string>;
-
namespace yang {
enum class NodeTypes {
Container,
diff --git a/src/static_schema.cpp b/src/static_schema.cpp
index d554cb9..aecad5a 100644
--- a/src/static_schema.cpp
+++ b/src/static_schema.cpp
@@ -73,15 +73,10 @@
std::set<identityRef_> StaticSchema::validIdentities(std::string_view module, std::string_view value)
{
- std::set<ModuleValuePair> identities;
- getIdentSet(ModuleNodePair{boost::optional<std::string>{module}, value}, identities);
- std::set<identityRef_> res;
+ std::set<identityRef_> identities;
+ getIdentSet(identityRef_{std::string{module}, std::string{value}}, identities);
- std::transform(identities.begin(), identities.end(), std::inserter(res, res.end()), [](const auto& identity) {
- return identityRef_{*identity.first, identity.second};
- });
-
- return res;
+ return identities;
}
void StaticSchema::addLeaf(const std::string& location, const std::string& name, const yang::LeafDataType& type)
@@ -96,15 +91,15 @@
m_modules.emplace(name);
}
-void StaticSchema::addIdentity(const std::optional<ModuleValuePair>& base, const ModuleValuePair& name)
+void StaticSchema::addIdentity(const std::optional<identityRef_>& base, const identityRef_& name)
{
if (base)
m_identities.at(base.value()).emplace(name);
- m_identities.emplace(name, std::set<ModuleValuePair>());
+ m_identities.emplace(name, std::set<identityRef_>());
}
-void StaticSchema::getIdentSet(const ModuleValuePair& ident, std::set<ModuleValuePair>& res) const
+void StaticSchema::getIdentSet(const identityRef_& ident, std::set<identityRef_>& res) const
{
res.insert(ident);
auto derivedIdentities = m_identities.at(ident);
@@ -135,15 +130,6 @@
return boost::get<yang::leaf>(children(locationString).at(node)).m_type;
}
-ModuleNodePair splitModuleNode(const std::string& input)
-{
- auto colonLocation = input.find_first_of(':');
- if (colonLocation != std::string::npos) {
- return ModuleNodePair{input.substr(0, colonLocation), input.substr(colonLocation + 1)};
- }
- throw std::logic_error("Tried to split a string without a colon (StaticSchema node names should always be stored with prefixes)");
-}
-
std::set<ModuleNodePair> StaticSchema::availableNodes(const boost::variant<dataPath_, schemaPath_, module_>& path, const Recursion recursion) const
{
if (recursion == Recursion::Recursive) {
diff --git a/src/static_schema.hpp b/src/static_schema.hpp
index 67bd7b9..5184ae6 100644
--- a/src/static_schema.hpp
+++ b/src/static_schema.hpp
@@ -69,16 +69,15 @@
void addLeaf(const std::string& location, const std::string& name, const yang::LeafDataType& type);
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);
+ void addIdentity(const std::optional<identityRef_>& base, const identityRef_& 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;
+ void getIdentSet(const identityRef_& ident, std::set<identityRef_>& res) const;
bool nodeExists(const std::string& location, const std::string& node) const;
std::unordered_map<std::string, std::unordered_map<std::string, NodeType>> m_nodes;
std::set<std::string> m_modules;
- // FIXME: Change the template arguments to identityRef_
- std::map<ModuleValuePair, std::set<ModuleValuePair>> m_identities;
+ std::map<identityRef_, std::set<identityRef_>> m_identities;
};
diff --git a/src/sysrepo_access.cpp b/src/sysrepo_access.cpp
index 7115ad7..b40b9f1 100644
--- a/src/sysrepo_access.cpp
+++ b/src/sysrepo_access.cpp
@@ -40,6 +40,13 @@
return std::string(value->data()->get_string());
case SR_ENUM_T:
return enum_{std::string(value->data()->get_enum())};
+ case SR_IDENTITYREF_T:
+ {
+ auto pair = splitModuleNode(value->data()->get_identityref());
+ return identityRef_{*pair.first, pair.second};
+ }
+ case SR_BINARY_T:
+ return binary_{value->data()->get_binary()};
case SR_DECIMAL64_T:
return value->data()->get_decimal64();
case SR_CONTAINER_T:
@@ -66,7 +73,7 @@
sysrepo::S_Val operator()(const identityRef_& value) const
{
- auto res = value.m_prefix.value().m_name + ":" + value.m_value;
+ auto res = value.m_prefix ? (value.m_prefix.value().m_name + ":" + value.m_value) : value.m_value;
return std::make_shared<sysrepo::Val>(res.c_str(), SR_IDENTITYREF_T);
}
diff --git a/src/utils.cpp b/src/utils.cpp
index 1ed25c7..665aedd 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -53,6 +53,15 @@
return schemaPath_{path.m_scope, decltype(schemaPath_::m_nodes)(path.m_nodes.begin(), path.m_nodes.end() - 1)};
}
+ModuleNodePair splitModuleNode(const std::string& input)
+{
+ auto colonLocation = input.find_first_of(':');
+ if (colonLocation != std::string::npos) {
+ return ModuleNodePair{input.substr(0, colonLocation), input.substr(colonLocation + 1)};
+ }
+ throw std::logic_error("Internal error: got module-unqualified node name");
+}
+
struct impl_leafDataTypeToString {
std::string operator()(const yang::String)
{
@@ -156,7 +165,7 @@
std::string operator()(const identityRef_& data) const
{
- return data.m_prefix.value().m_name + ":" + data.m_value;
+ return data.m_prefix ? (data.m_prefix.value().m_name + ":" + data.m_value) : data.m_value;
}
std::string operator()(const special_& data) const
diff --git a/src/utils.hpp b/src/utils.hpp
index 7e62ae5..781170d 100644
--- a/src/utils.hpp
+++ b/src/utils.hpp
@@ -19,6 +19,7 @@
std::string stripLastNodeFromPath(const std::string& path);
schemaPath_ pathWithoutLastNode(const schemaPath_& path);
dataPath_ pathWithoutLastNode(const dataPath_& path);
+ModuleNodePair splitModuleNode(const std::string& input);
std::string leafDataTypeToString(const yang::LeafDataType& type);
std::string fullNodeName(const schemaPath_& location, const ModuleNodePair& pair);
std::string fullNodeName(const dataPath_& location, const ModuleNodePair& pair);
diff --git a/tests/datastore_access.cpp b/tests/datastore_access.cpp
index d5f3e12..51bcd81 100644
--- a/tests/datastore_access.cpp
+++ b/tests/datastore_access.cpp
@@ -319,6 +319,46 @@
REQUIRE(datastore.getItems("/example-schema:leafDecimal") == expected);
}
+ SECTION("unions")
+ {
+ datastore.setLeaf("/example-schema:unionIntString", int32_t{10});
+ REQUIRE_CALL(mock, write("/example-schema:unionIntString", std::nullopt, "10"s));
+ datastore.commitChanges();
+ DatastoreAccess::Tree expected {
+ {"/example-schema:unionIntString", int32_t{10}},
+ };
+ REQUIRE(datastore.getItems("/example-schema:unionIntString") == expected);
+ }
+
+ SECTION("identityref") {
+ datastore.setLeaf("/example-schema:beast", identityRef_{"example-schema", "Mammal"});
+ REQUIRE_CALL(mock, write("/example-schema:beast", std::nullopt, "example-schema:Mammal"s));
+ datastore.commitChanges();
+ DatastoreAccess::Tree expected {
+ {"/example-schema:beast", identityRef_{"example-schema", "Mammal"}},
+ };
+ REQUIRE(datastore.getItems("/example-schema:beast") == expected);
+
+ datastore.setLeaf("/example-schema:beast", identityRef_{"Whale"});
+ REQUIRE_CALL(mock, write("/example-schema:beast", "example-schema:Mammal", "example-schema:Whale"s));
+ datastore.commitChanges();
+ expected = {
+ {"/example-schema:beast", identityRef_{"example-schema", "Whale"}},
+ };
+ REQUIRE(datastore.getItems("/example-schema:beast") == expected);
+ }
+
+ SECTION("binary")
+ {
+ datastore.setLeaf("/example-schema:blob", binary_{"cHduegByIQ=="s});
+ REQUIRE_CALL(mock, write("/example-schema:blob", std::nullopt, "cHduegByIQ=="s));
+ datastore.commitChanges();
+ DatastoreAccess::Tree expected {
+ {"/example-schema:blob", binary_{"cHduegByIQ=="s}},
+ };
+ REQUIRE(datastore.getItems("/example-schema:blob") == expected);
+ }
+
SECTION("operational data")
{
MockDataSupplier mockOpsData;
diff --git a/tests/example-schema.yang b/tests/example-schema.yang
index ac7724a..5d84eda 100644
--- a/tests/example-schema.yang
+++ b/tests/example-schema.yang
@@ -214,4 +214,33 @@
type int32;
config false;
}
+
+ identity Animal {
+ }
+
+ identity Mammal {
+ base "Animal";
+ }
+
+ identity Dog {
+ base "Mammal";
+ }
+
+ identity Whale {
+ base "Mammal";
+ }
+
+ identity Velociraptor {
+ base "Animal";
+ }
+
+ leaf beast {
+ type identityref {
+ base "Animal";
+ }
+ }
+
+ leaf blob {
+ type binary;
+ }
}
diff --git a/tests/leaf_editing.cpp b/tests/leaf_editing.cpp
index 64abbf2..986139d 100644
--- a/tests/leaf_editing.cpp
+++ b/tests/leaf_editing.cpp
@@ -37,11 +37,11 @@
schema->addLeaf("/", "mod:leafUint32", yang::Uint32{});
schema->addLeaf("/", "mod:leafUint64", yang::Uint64{});
schema->addLeaf("/", "mod:leafBinary", yang::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->addIdentity(std::nullopt, identityRef_{"mod", "food"});
+ schema->addIdentity(std::nullopt, identityRef_{"mod", "vehicle"});
+ schema->addIdentity(identityRef_{"mod", "food"}, identityRef_{"mod", "pizza"});
+ schema->addIdentity(identityRef_{"mod", "food"}, identityRef_{"mod", "spaghetti"});
+ schema->addIdentity(identityRef_{"mod", "pizza"}, identityRef_{"pizza-module", "hawaii"});
schema->addLeaf("/", "mod:foodIdentRef", yang::IdentityRef{schema->validIdentities("mod", "food")});
schema->addLeaf("/", "mod:pizzaIdentRef", yang::IdentityRef{schema->validIdentities("mod", "pizza")});
schema->addLeaf("/mod:contA", "mod:identInCont", yang::IdentityRef{schema->validIdentities("mod", "pizza")});