Merge "Improve error message for enum values"
diff --git a/src/ast_handlers.hpp b/src/ast_handlers.hpp
index 94121b7..82d0b1d 100644
--- a/src/ast_handlers.hpp
+++ b/src/ast_handlers.hpp
@@ -333,6 +333,7 @@
{
auto& parserContext = x3::get<parser_context_tag>(context);
parserContext.m_suggestions.clear();
+ parserContext.m_completionSuffix.clear();
}
};
@@ -561,7 +562,32 @@
const auto& schema = parserContext.m_schema;
parserContext.m_completionIterator = begin;
- parserContext.m_suggestions = schema.childNodes(parserContext.m_curPath, Recursion::NonRecursive);
+ auto suggestions = schema.childNodes(parserContext.m_curPath, Recursion::NonRecursive);
+ std::set<std::string> suffixesAdded;
+ std::transform(suggestions.begin(), suggestions.end(),
+ std::inserter(suffixesAdded, suffixesAdded.end()),
+ [&parserContext, &schema] (auto it) {
+ ModuleNodePair node;
+ if (auto colonPos = it.find(":"); colonPos != it.npos) {
+ node.first = it.substr(0, colonPos);
+ node.second = it.substr(colonPos + 1, node.second.npos);
+ } else {
+ node.first = boost::none;
+ node.second = it;
+ }
+
+ if (schema.isLeaf(parserContext.m_curPath, node)) {
+ return it + " ";
+ }
+ if (schema.isContainer(parserContext.m_curPath, node)) {
+ return it + "/";
+ }
+ if (schema.isList(parserContext.m_curPath, node)) {
+ return it + "[";
+ }
+ return it;
+ });
+ parserContext.m_suggestions = suffixesAdded;
}
};
@@ -598,16 +624,6 @@
}
};
-struct suggestKeysStart_class {
- template <typename T, typename Iterator, typename Context>
- void on_success(Iterator const&, Iterator const&, T&, Context const& context)
- {
- auto& parserContext = x3::get<parser_context_tag>(context);
-
- parserContext.m_completionSuffix = "[";
- }
-};
-
struct commandNamesVisitor {
template <typename T>
auto operator()(boost::type<T>)
diff --git a/src/grammars.hpp b/src/grammars.hpp
index 4fd7b15..0a259b8 100644
--- a/src/grammars.hpp
+++ b/src/grammars.hpp
@@ -71,7 +71,6 @@
x3::rule<initializePath_class, x3::unused_type> const initializePath = "initializePath";
x3::rule<createPathSuggestions_class, x3::unused_type> const createPathSuggestions = "createPathSuggestions";
x3::rule<createKeySuggestions_class, x3::unused_type> const createKeySuggestions = "createKeySuggestions";
-x3::rule<suggestKeysStart_class, x3::unused_type> const suggestKeysStart = "suggestKeysStart";
x3::rule<suggestKeysEnd_class, x3::unused_type> const suggestKeysEnd = "suggestKeysEnd";
x3::rule<createCommandSuggestions_class, x3::unused_type> const createCommandSuggestions = "createCommandSuggestions";
x3::rule<completing_class, x3::unused_type> const completing = "completing";
@@ -118,9 +117,6 @@
auto const createKeySuggestions_def =
x3::eps;
-auto const suggestKeysStart_def =
- x3::eps;
-
auto const suggestKeysEnd_def =
x3::eps;
@@ -148,7 +144,7 @@
*keyValueWrapper;
auto const listElement_def =
- listPrefix >> -(!char_('[') >> suggestKeysStart) >> &char_('[') > listSuffix;
+ listPrefix >> &char_('[') > listSuffix;
auto const list_def =
node_identifier >> !char_('[');
@@ -405,7 +401,6 @@
BOOST_SPIRIT_DEFINE(command)
BOOST_SPIRIT_DEFINE(createPathSuggestions)
BOOST_SPIRIT_DEFINE(createKeySuggestions)
-BOOST_SPIRIT_DEFINE(suggestKeysStart)
BOOST_SPIRIT_DEFINE(suggestKeysEnd)
BOOST_SPIRIT_DEFINE(createCommandSuggestions)
BOOST_SPIRIT_DEFINE(completing)
diff --git a/src/main.cpp b/src/main.cpp
index f702cb8..ec1b45c 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -62,7 +62,14 @@
while (true) {
auto line = lineEditor.input(parser.currentNode() + "> ");
if (!line) {
- break;
+ // If user pressed CTRL-C to abort the line, errno gets set to EAGAIN.
+ // If user pressed CTRL-D (for EOF), errno doesn't get set to EAGAIN, so we exit the program.
+ // I have no idea why replxx uses errno for this.
+ if (errno == EAGAIN) {
+ continue;
+ } else {
+ break;
+ }
}
std::locale C_locale("C");
diff --git a/src/static_schema.cpp b/src/static_schema.cpp
index a3f59aa..60cddf9 100644
--- a/src/static_schema.cpp
+++ b/src/static_schema.cpp
@@ -107,6 +107,8 @@
void StaticSchema::addLeaf(const std::string& location, const std::string& name, const yang::LeafDataTypes& type)
{
m_nodes.at(location).emplace(name, yang::leaf{type, {}, {}, {}});
+ std::string key = joinPaths(location, name);
+ m_nodes.emplace(key, std::unordered_map<std::string, NodeType>());
}
void StaticSchema::addLeafEnum(const std::string& location, const std::string& name, std::set<std::string> enumValues)
@@ -115,6 +117,8 @@
toAdd.m_type = yang::LeafDataTypes::Enum;
toAdd.m_enumValues = enumValues;
m_nodes.at(location).emplace(name, toAdd);
+ std::string key = joinPaths(location, name);
+ m_nodes.emplace(key, std::unordered_map<std::string, NodeType>());
}
void StaticSchema::addLeafIdentityRef(const std::string& location, const std::string& name, const ModuleValuePair& base)
@@ -124,6 +128,8 @@
toAdd.m_type = yang::LeafDataTypes::IdentityRef;
toAdd.m_identBase = base;
m_nodes.at(location).emplace(name, toAdd);
+ std::string key = joinPaths(location, name);
+ m_nodes.emplace(key, std::unordered_map<std::string, NodeType>());
}
void StaticSchema::addLeafRef(const std::string& location, const std::string& name, const std::string& source)
@@ -132,6 +138,8 @@
toAdd.m_type = yang::LeafDataTypes::LeafRef;
toAdd.m_leafRefSource = source;
m_nodes.at(location).emplace(name, toAdd);
+ std::string key = joinPaths(location, name);
+ m_nodes.emplace(key, std::unordered_map<std::string, NodeType>());
}
void StaticSchema::addModule(const std::string& name)
diff --git a/src/sysrepo_access.cpp b/src/sysrepo_access.cpp
index 2c80c2d..f84bb91 100644
--- a/src/sysrepo_access.cpp
+++ b/src/sysrepo_access.cpp
@@ -93,8 +93,13 @@
return fetchSchema(moduleName, revision, submodule);
});
- for (const auto& it : listImplementedSchemas()) {
- m_schema->loadModule(it);
+ for (const auto& it : listSchemas()) {
+ if (it->implemented()) {
+ m_schema->loadModule(it->module_name());
+ for (unsigned int i = 0; i < it->enabled_feature_cnt(); i++) {
+ m_schema->enableFeature(it->module_name(), it->enabled_features(i));
+ }
+ }
}
}
@@ -205,9 +210,9 @@
return schema;
}
-std::vector<std::string> SysrepoAccess::listImplementedSchemas()
+std::vector<std::shared_ptr<sysrepo::Yang_Schema>> SysrepoAccess::listSchemas()
{
- std::vector<std::string> res;
+ std::vector<sysrepo::S_Yang_Schema> res;
std::shared_ptr<sysrepo::Yang_Schemas> schemas;
try {
schemas = m_session->list_schemas();
@@ -216,8 +221,7 @@
}
for (unsigned int i = 0; i < schemas->schema_cnt(); i++) {
auto schema = schemas->schema(i);
- if (schema->implemented())
- res.push_back(schema->module_name());
+ res.push_back(schema);
}
return res;
}
diff --git a/src/sysrepo_access.hpp b/src/sysrepo_access.hpp
index 8df1153..9861ab4 100644
--- a/src/sysrepo_access.hpp
+++ b/src/sysrepo_access.hpp
@@ -18,6 +18,7 @@
namespace sysrepo {
class Connection;
class Session;
+class Yang_Schema;
}
class Schema;
@@ -43,7 +44,7 @@
[[noreturn]] void reportErrors();
std::string fetchSchema(const char* module, const char* revision, const char* submodule);
- std::vector<std::string> listImplementedSchemas();
+ std::vector<std::shared_ptr<sysrepo::Yang_Schema>> listSchemas();
std::shared_ptr<sysrepo::Connection> m_connection;
std::shared_ptr<sysrepo::Session> m_session;
diff --git a/src/yang_schema.cpp b/src/yang_schema.cpp
index 2619943..549393a 100644
--- a/src/yang_schema.cpp
+++ b/src/yang_schema.cpp
@@ -135,8 +135,14 @@
enm = type->info()->enums()->enm();
}
+ std::vector<libyang::S_Type_Enum> enabled;
+ std::copy_if(enm.begin(), enm.end(), std::back_inserter(enabled), [] (const libyang::S_Type_Enum& it) {
+ auto iffeatures = it->iffeature();
+ return std::all_of(iffeatures.begin(), iffeatures.end(), [] (auto it) {return it->value();});
+ });
+
std::set<std::string> enumSet;
- std::transform(enm.begin(), enm.end(), std::inserter(enumSet, enumSet.end()), [](auto it) { return it->name(); });
+ std::transform(enabled.begin(), enabled.end(), std::inserter(enumSet, enumSet.end()), [](auto it) { return it->name(); });
return enumSet;
}
@@ -188,11 +194,8 @@
return keys.find(key) != keys.end();
}
-libyang::S_Schema_Node YangSchema::getSchemaNode(const schemaPath_& location, const ModuleNodePair& node) const
+libyang::S_Schema_Node YangSchema::impl_getSchemaNode(const std::string& node) const
{
- std::string absPath = location.m_nodes.empty() ? "" : "/";
- absPath += pathToAbsoluteSchemaString(location) + "/" + fullNodeName(location, node);
-
// If no node is found find_path prints an error message, so we have to
// disable logging
// https://github.com/CESNET/libyang/issues/753
@@ -205,10 +208,24 @@
[&oldOptions]() {
libyang::set_log_options(oldOptions);
});
- return m_context->get_node(nullptr, absPath.c_str());
+ return m_context->get_node(nullptr, node.c_str());
}
}
+
+libyang::S_Schema_Node YangSchema::getSchemaNode(const std::string& node) const
+{
+ return impl_getSchemaNode(node);
+}
+
+libyang::S_Schema_Node YangSchema::getSchemaNode(const schemaPath_& location, const ModuleNodePair& node) const
+{
+ std::string absPath = location.m_nodes.empty() ? "" : "/";
+ absPath += pathToAbsoluteSchemaString(location) + "/" + fullNodeName(location, node);
+
+ return impl_getSchemaNode(absPath);
+}
+
const std::set<std::string> YangSchema::listKeys(const schemaPath_& location, const ModuleNodePair& node) const
{
std::set<std::string> keys;
@@ -309,9 +326,8 @@
nodes = m_context->data_instantiables(0);
} else {
const auto absolutePath = "/" + pathToAbsoluteSchemaString(path);
- const auto set = m_context->find_path(absolutePath.c_str());
- const auto schemaSet = set->schema();
- nodes = (*schemaSet.begin())->child_instantiables(0);
+ const auto node = getSchemaNode(absolutePath);
+ nodes = node->child_instantiables(0);
}
for (const auto node : nodes) {
@@ -360,6 +376,11 @@
m_context->load_module(moduleName.c_str());
}
+void YangSchema::enableFeature(const std::string& moduleName, const std::string& featureName)
+{
+ m_context->get_module(moduleName.c_str())->feature_enable(featureName.c_str());
+}
+
void YangSchema::registerModuleCallback(const std::function<std::string(const char*, const char*, const char*)>& clb)
{
auto lambda = [clb](const char* mod_name, const char* mod_revision, const char* submod_name, const char* submod_revision) {
diff --git a/src/yang_schema.hpp b/src/yang_schema.hpp
index 12a250c..14b0f1e 100644
--- a/src/yang_schema.hpp
+++ b/src/yang_schema.hpp
@@ -50,6 +50,9 @@
/** @short Loads a module called moduleName. */
void loadModule(const std::string& moduleName);
+ /** @short Enables a feature called featureName on a module called moduleName. */
+ void enableFeature(const std::string& moduleName, const std::string& featureName);
+
/** @short Adds a new module passed as a YANG string. */
void addSchemaString(const char* schema);
@@ -69,6 +72,11 @@
/** @short Returns a set of nodes, that match the location and name criteria. */
/** @short Returns a single Schema_Node if the criteria matches only one, otherwise nullptr. */
+ std::shared_ptr<libyang::Schema_Node> getSchemaNode(const std::string& node) const;
+
+ /** @short Returns a single Schema_Node if the criteria matches only one, otherwise nullptr. */
std::shared_ptr<libyang::Schema_Node> getSchemaNode(const schemaPath_& location, const ModuleNodePair& node) const;
std::shared_ptr<libyang::Context> m_context;
+
+ std::shared_ptr<libyang::Schema_Node> impl_getSchemaNode(const std::string& node) const;
};
diff --git a/submodules/dependencies b/submodules/dependencies
index 7f378d1..2d91a55 160000
--- a/submodules/dependencies
+++ b/submodules/dependencies
@@ -1 +1 @@
-Subproject commit 7f378d15b8194e0c84ed00ea07a5c3e4f9565838
+Subproject commit 2d91a55add68ebcc21322956e51ebf51458fc2c3
diff --git a/tests/path_completion.cpp b/tests/path_completion.cpp
index 5adb7ff..c68cca0 100644
--- a/tests/path_completion.cpp
+++ b/tests/path_completion.cpp
@@ -51,25 +51,25 @@
SECTION("ls ")
{
input = "ls ";
- expected = {"example:ano", "example:anoda", "example:bota", "example:leafInt", "example:list", "example:ovoce", "example:ovocezelenina", "example:twoKeyList", "second:amelie"};
+ expected = {"example:ano/", "example:anoda/", "example:bota/", "example:leafInt ", "example:list[", "example:ovoce[", "example:ovocezelenina[", "example:twoKeyList[", "second:amelie/"};
}
SECTION("ls e")
{
input = "ls e";
- expected = {"example:ano", "example:anoda", "example:bota", "example:leafInt", "example:list", "example:ovoce", "example:ovocezelenina", "example:twoKeyList"};
+ expected = {"example:ano/", "example:anoda/", "example:bota/", "example:leafInt ", "example:list[", "example:ovoce[", "example:ovocezelenina[", "example:twoKeyList["};
}
SECTION("ls example:ano")
{
input = "ls example:ano";
- expected = {"example:ano", "example:anoda"};
+ expected = {"example:ano/", "example:anoda/"};
}
SECTION("ls example:ano/example:a")
{
input = "ls example:ano/example:a";
- expected = {"example:a2"};
+ expected = {"example:a2/"};
}
SECTION("ls x")
@@ -81,32 +81,44 @@
SECTION("ls /")
{
input = "ls /";
- expected = {"example:ano", "example:anoda", "example:bota", "example:leafInt", "example:list", "example:ovoce", "example:ovocezelenina", "example:twoKeyList", "second:amelie"};
+ expected = {"example:ano/", "example:anoda/", "example:bota/", "example:leafInt ", "example:list[", "example:ovoce[", "example:ovocezelenina[", "example:twoKeyList[", "second:amelie/"};
}
SECTION("ls /e")
{
input = "ls /e";
- expected = {"example:ano", "example:anoda", "example:bota", "example:leafInt", "example:list", "example:ovoce", "example:ovocezelenina", "example:twoKeyList"};
+ expected = {"example:ano/", "example:anoda/", "example:bota/", "example:leafInt ", "example:list[", "example:ovoce[", "example:ovocezelenina[", "example:twoKeyList["};
}
+ SECTION("ls example:bota")
+ {
+ input = "ls example:bota";
+ expected = {"example:bota/"};
+ }
+
+ SECTION("ls /example:bota")
+ {
+ input = "ls /example:bota";
+ expected = {"example:bota/"};
+ }
+
SECTION("ls /s")
{
input = "ls /s";
- expected = {"second:amelie"};
+ expected = {"second:amelie/"};
}
SECTION("ls /example:list[number=3]/")
{
input = "ls /example:list[number=3]/";
- expected = {"example:contInList"};
+ expected = {"example:contInList/"};
}
SECTION("ls /example:list[number=3]/c")
{
input = "ls /example:list[number=3]/e";
- expected = {"example:contInList"};
+ expected = {"example:contInList/"};
}
SECTION("ls /example:list[number=3]/a")
@@ -121,7 +133,7 @@
SECTION("cd example:lis")
{
input = "cd example:lis";
- expected = {"example:list"};
+ expected = {"example:list["};
}
SECTION("set example:list")
@@ -211,7 +223,7 @@
SECTION("cd example:ovoce")
{
input = "cd example:ovoce";
- expected = {"example:ovoce", "example:ovocezelenina"};
+ expected = {"example:ovoce[", "example:ovocezelenina["};
}
}
diff --git a/tests/yang.cpp b/tests/yang.cpp
index 156d94b..2ca1704 100644
--- a/tests/yang.cpp
+++ b/tests/yang.cpp
@@ -6,6 +6,7 @@
*
*/
+#include <experimental/iterator>
#include "trompeloeil_doctest.h"
#include "yang_schema.hpp"
@@ -249,8 +250,30 @@
}
}
+ feature bigPizzas;
+
+ leaf pizzaSize {
+ type enumeration {
+ enum large {
+ if-feature "bigPizzas";
+ }
+ enum medium;
+ enum small;
+ }
+ }
+
})";
+namespace std {
+std::ostream& operator<<(std::ostream& s, const std::set<std::string> set)
+{
+ s << std::endl << "{";
+ std::copy(set.begin(), set.end(), std::experimental::make_ostream_joiner(s, ", "));
+ s << "}" << std::endl;
+ return s;
+}
+}
+
TEST_CASE("yangschema")
{
using namespace std::string_view_literals;
@@ -423,6 +446,28 @@
value = "data";
}
+ SECTION("pizzaSize")
+ {
+ node.first = "example-schema";
+ node.second = "pizzaSize";
+
+ SECTION("small")
+ {
+ value = "small";
+
+ }
+ SECTION("medium")
+ {
+ value = "medium";
+ }
+
+ SECTION("large")
+ {
+ ys.enableFeature("example-schema", "bigPizzas");
+ value = "large";
+ }
+ }
+
REQUIRE(ys.leafEnumHasValue(path, node, value));
}
SECTION("leafIdentityIsValid")
@@ -681,7 +726,8 @@
"example-schema:_list", "example-schema:twoKeyList", "second-schema:bla",
"example-schema:carry", "example-schema:zero", "example-schema:direction",
"example-schema:interrupt",
- "example-schema:ethernet", "example-schema:loopback"};
+ "example-schema:ethernet", "example-schema:loopback",
+ "example-schema:pizzaSize"};
}
SECTION("example-schema:a")
@@ -696,6 +742,12 @@
set = {"bla2"};
}
+ SECTION("example-schema:ethernet")
+ {
+ path.m_nodes.push_back(schemaNode_(module_{"example-schema"}, container_("ethernet")));
+ set = {"ip"};
+ }
+
REQUIRE(ys.childNodes(path, Recursion::NonRecursive) == set);
}
}
@@ -852,5 +904,15 @@
REQUIRE(!ys.isLeaf(path, node));
REQUIRE(!ys.isContainer(path, node));
}
+
+ SECTION("enum is disabled by if-feature if feature is not enabled")
+ {
+ node.first = "example-schema";
+ node.second = "pizzaSize";
+
+ std::string value = "large";
+
+ REQUIRE(!ys.leafEnumHasValue(path, node, value));
+ }
}
}