add presence containers
Change-Id: Ic8e33d68e496deae9dfe4c3e5ebcecbd45ee31b2
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3819930..dd82c6e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -109,6 +109,7 @@
target_link_libraries(test_${fname} TestCatchIntegration)
endmacro()
cli_test(cd)
+ cli_test(presence_containers)
endif()
diff --git a/src/ast.cpp b/src/ast.cpp
index 9345192..0e40b9c 100644
--- a/src/ast.cpp
+++ b/src/ast.cpp
@@ -40,3 +40,13 @@
{
return this->m_path == b.m_path;
}
+
+bool create_::operator==(const create_& b) const
+{
+ return this->m_path == b.m_path;
+}
+
+bool delete_::operator==(const delete_& b) const
+{
+ return this->m_path == b.m_path;
+}
diff --git a/src/ast.hpp b/src/ast.hpp
index 90e007a..417c3d7 100644
--- a/src/ast.hpp
+++ b/src/ast.hpp
@@ -81,7 +81,21 @@
path_ m_path;
};
+struct create_ : x3::position_tagged {
+ bool operator==(const create_& b) const;
+ path_ m_path;
+};
+
+struct delete_ : x3::position_tagged {
+ bool operator==(const delete_& b) const;
+ path_ m_path;
+};
+
+using command_ = boost::variant<cd_, create_, delete_>;
+
BOOST_FUSION_ADAPT_STRUCT(container_, m_name)
BOOST_FUSION_ADAPT_STRUCT(listElement_, m_name, m_keys)
BOOST_FUSION_ADAPT_STRUCT(path_, m_nodes)
BOOST_FUSION_ADAPT_STRUCT(cd_, m_path)
+BOOST_FUSION_ADAPT_STRUCT(create_, m_path)
+BOOST_FUSION_ADAPT_STRUCT(delete_, m_path)
diff --git a/src/ast_handlers.hpp b/src/ast_handlers.hpp
index 0c7f1c3..1feeeab 100644
--- a/src/ast_handlers.hpp
+++ b/src/ast_handlers.hpp
@@ -110,7 +110,6 @@
} else {
_pass(context) = false;
}
-
}
};
@@ -156,3 +155,61 @@
return x3::error_handler_result::fail;
}
};
+
+struct presenceContainerPathHandler {
+ 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;
+ try {
+ container_ cont = boost::get<container_>(ast.m_path.m_nodes.back());
+ path_ location{decltype(path_::m_nodes)(parserContext.m_curPath.m_nodes.begin(),
+ parserContext.m_curPath.m_nodes.end() - 1)};
+
+ if (!schema.isPresenceContainer(location, cont.m_name)) {
+ _pass(context) = false;
+ return;
+ }
+ } catch (boost::bad_get&) {
+ _pass(context) = false;
+ return;
+ }
+ }
+
+ template <typename Iterator, typename Exception, typename Context>
+ x3::error_handler_result on_error(Iterator&, Iterator const&, Exception const& x, Context const& context)
+ {
+ auto& parserContext = x3::get<parser_context_tag>(context);
+ auto& error_handler = x3::get<x3::error_handler_tag>(context).get();
+ std::string message = "This isn't a path to a presence container.";
+ if (parserContext.m_errorHandled) // someone already handled our error
+ return x3::error_handler_result::fail;
+
+ parserContext.m_errorHandled = true;
+ error_handler(x.where(), message);
+ return x3::error_handler_result::fail;
+ }
+};
+
+struct create_class : public presenceContainerPathHandler {
+};
+
+struct delete_class : public presenceContainerPathHandler {
+};
+
+struct command_class {
+ template <typename Iterator, typename Exception, typename Context>
+ x3::error_handler_result on_error(Iterator&, Iterator const&, Exception const& x, Context const& context)
+ {
+ auto& parserContext = x3::get<parser_context_tag>(context);
+ auto& error_handler = x3::get<x3::error_handler_tag>(context).get();
+ std::string message = "Couldn't parse command.";
+ if (parserContext.m_errorHandled) // someone already handled our error
+ return x3::error_handler_result::fail;
+
+ parserContext.m_errorHandled = true;
+ error_handler(x.where(), message);
+ return x3::error_handler_result::fail;
+ }
+};
diff --git a/src/grammars.hpp b/src/grammars.hpp
index ef6b60f..30f69f1 100644
--- a/src/grammars.hpp
+++ b/src/grammars.hpp
@@ -21,6 +21,9 @@
x3::rule<container_class, container_> const container = "container";
x3::rule<path_class, path_> const path = "path";
x3::rule<cd_class, cd_> const cd = "cd";
+x3::rule<create_class, create_> const create = "create";
+x3::rule<delete_class, delete_> const delete_rule = "delete_rule";
+x3::rule<command_class, command_> const command = "command";
auto const keyValue_def =
@@ -53,6 +56,15 @@
auto const cd_def =
lit("cd") > x3::omit[x3::no_skip[space]] > path >> x3::eoi;
+auto const create_def =
+ lit("create") > x3::omit[x3::no_skip[space]] > path >> x3::eoi;
+
+auto const delete_rule_def =
+ lit("delete") > x3::omit[x3::no_skip[space]] > path >> x3::eoi;
+
+auto const command_def =
+ cd | create | delete_rule;
+
BOOST_SPIRIT_DEFINE(keyValue)
BOOST_SPIRIT_DEFINE(identifier)
BOOST_SPIRIT_DEFINE(listPrefix)
@@ -62,3 +74,6 @@
BOOST_SPIRIT_DEFINE(container)
BOOST_SPIRIT_DEFINE(path)
BOOST_SPIRIT_DEFINE(cd)
+BOOST_SPIRIT_DEFINE(create)
+BOOST_SPIRIT_DEFINE(delete_rule)
+BOOST_SPIRIT_DEFINE(command)
diff --git a/src/interpreter.cpp b/src/interpreter.cpp
index ce01c66..5c132d1 100644
--- a/src/interpreter.cpp
+++ b/src/interpreter.cpp
@@ -6,6 +6,7 @@
*
*/
+#include <iostream>
#include "interpreter.hpp"
void Interpreter::operator()(const cd_& cd) const
@@ -13,7 +14,17 @@
m_parser.changeNode(cd.m_path);
}
-Interpreter::Interpreter(Parser& parser)
+void Interpreter::operator()(const create_& create) const
+{
+ std::cout << "Presence container " << boost::get<container_>(create.m_path.m_nodes.back()).m_name << " created." << std::endl;
+}
+
+void Interpreter::operator()(const delete_& delet) const
+{
+ std::cout << "Presence container " << boost::get<container_>(delet.m_path.m_nodes.back()).m_name << " deleted." << std::endl;
+}
+
+Interpreter::Interpreter(Parser& parser, Schema&)
: m_parser(parser)
{
}
diff --git a/src/interpreter.hpp b/src/interpreter.hpp
index 72c839e..a4a5cc0 100644
--- a/src/interpreter.hpp
+++ b/src/interpreter.hpp
@@ -12,9 +12,12 @@
#include "parser.hpp"
struct Interpreter : boost::static_visitor<void> {
- Interpreter(Parser &m_parser);
+ Interpreter(Parser& parser, Schema&);
void operator()(const cd_&) const;
+ void operator()(const create_&) const;
+ void operator()(const delete_&) const;
+
private:
Parser& m_parser;
};
diff --git a/src/main.cpp b/src/main.cpp
index 98c68ca..4fb9d8d 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -36,7 +36,6 @@
using x3::lexeme;
using x3::lit;
-using command = boost::variant<cd_>;
int main(int argc, char* argv[])
{
@@ -48,14 +47,14 @@
std::cout << "Welcome to netconf-cli" << std::endl;
Schema schema;
- schema.addContainer("", "a");
+ schema.addContainer("", "a", yang::ContainerTraits::Presence);
schema.addContainer("", "b");
schema.addContainer("a", "a2");
- schema.addContainer("b", "b2");
- schema.addContainer("a/a2", "a3");
+ schema.addContainer("b", "b2", yang::ContainerTraits::Presence);
+ schema.addContainer("a/a2", "a3", yang::ContainerTraits::Presence);
schema.addContainer("b/b2", "b3");
schema.addList("", "list", {"number"});
- schema.addContainer("list", "contInList");
+ schema.addContainer("list", "contInList", yang::ContainerTraits::Presence);
schema.addList("", "twoKeyList", {"number", "name"});
Parser parser(schema);
@@ -67,8 +66,8 @@
break;
try {
- command cmd = parser.parseCommand(input, std::cout);
- boost::apply_visitor(Interpreter(parser), cmd);
+ command_ cmd = parser.parseCommand(input, std::cout);
+ boost::apply_visitor(Interpreter(parser, schema), cmd);
} catch (InvalidCommandException& ex) {
std::cerr << ex.what() << std::endl;
}
diff --git a/src/parser.cpp b/src/parser.cpp
index b8ac30d..f26b164 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -41,9 +41,9 @@
}
};
-cd_ Parser::parseCommand(const std::string& line, std::ostream& errorStream)
+command_ Parser::parseCommand(const std::string& line, std::ostream& errorStream)
{
- cd_ parsedCommand;
+ command_ parsedCommand;
ParserContext ctx(m_schema, m_curDir);
auto it = line.begin();
@@ -51,7 +51,7 @@
auto grammar =
x3::with<parser_context_tag>(ctx)[
- x3::with<x3::error_handler_tag>(std::ref(errorHandler))[cd]
+ x3::with<x3::error_handler_tag>(std::ref(errorHandler))[command]
];
bool result = x3::phrase_parse(it, line.end(), grammar, space, parsedCommand);
@@ -81,4 +81,3 @@
return res;
}
-
diff --git a/src/parser.hpp b/src/parser.hpp
index feb7911..0a253c1 100644
--- a/src/parser.hpp
+++ b/src/parser.hpp
@@ -9,15 +9,7 @@
#pragma once
#include <boost/spirit/home/x3.hpp>
#include "grammars.hpp"
-namespace x3 = boost::spirit::x3;
-namespace ascii = boost::spirit::x3::ascii;
-using Cmd = std::vector<std::string>;
-using ascii::space;
-using x3::_attr;
-using x3::alpha;
-using x3::char_;
-using x3::lexeme;
-using x3::lit;
+
class InvalidCommandException : public std::invalid_argument {
public:
@@ -35,7 +27,7 @@
class Parser {
public:
Parser(const Schema& schema);
- cd_ parseCommand(const std::string& line, std::ostream& errorStream);
+ command_ parseCommand(const std::string& line, std::ostream& errorStream);
void changeNode(const path_& name);
std::string currentNode() const;
diff --git a/src/schema.cpp b/src/schema.cpp
index 1760b5d..fa72f81 100644
--- a/src/schema.cpp
+++ b/src/schema.cpp
@@ -6,7 +6,6 @@
*
*/
-#include <iostream>
#include "schema.hpp"
#include "utils.hpp"
@@ -60,9 +59,9 @@
return children(locationString).at(name).type() == typeid(yang::container);
}
-void Schema::addContainer(const std::string& location, const std::string& name)
+void Schema::addContainer(const std::string& location, const std::string& name, yang::ContainerTraits isPresence)
{
- m_nodes.at(location).emplace(name, yang::container{});
+ m_nodes.at(location).emplace(name, yang::container{isPresence});
//create a new set of children for the new node
std::string key = joinPaths(location, name);
@@ -108,3 +107,11 @@
m_nodes.emplace(name, std::unordered_map<std::string, NodeType>());
}
+
+bool Schema::isPresenceContainer(const path_& location, const std::string& name) const
+{
+ if (!isContainer(location, name))
+ return false;
+ std::string locationString = pathToString(location);
+ return boost::get<yang::container>(children(locationString).at(name)).m_presence == yang::ContainerTraits::Presence;
+}
diff --git a/src/schema.hpp b/src/schema.hpp
index 7d6e486..9c7114b 100644
--- a/src/schema.hpp
+++ b/src/schema.hpp
@@ -15,7 +15,12 @@
#include "ast.hpp"
namespace yang {
+enum class ContainerTraits {
+ Presence,
+ None,
+};
struct container {
+ yang::ContainerTraits m_presence;
};
struct list {
std::set<std::string> m_keys;
@@ -23,7 +28,6 @@
}
-
using NodeType = boost::variant<yang::container, yang::list>;
@@ -42,11 +46,12 @@
bool nodeExists(const std::string& location, const std::string& name) const;
bool isContainer(const path_& location, const std::string& name) const;
- void addContainer(const std::string& location, const std::string& name);
+ void addContainer(const std::string& location, const std::string& name, yang::ContainerTraits isPresence = yang::ContainerTraits::None);
const std::set<std::string>& listKeys(const path_& location, const std::string& name) const;
bool listHasKey(const path_& location, const std::string& name, const std::string& key) const;
bool isList(const path_& location, const std::string& name) const;
void addList(const std::string& location, const std::string& name, const std::set<std::string>& keys);
+ bool isPresenceContainer(const path_& location, const std::string& name) const;
private:
const std::unordered_map<std::string, NodeType>& children(const std::string& name) const;
diff --git a/tests/cd.cpp b/tests/cd.cpp
index 83a1f98..2cef615 100644
--- a/tests/cd.cpp
+++ b/tests/cd.cpp
@@ -125,8 +125,9 @@
}
}
- cd_ command = parser.parseCommand(input, errorStream);
- REQUIRE(command == expected);
+ command_ command = parser.parseCommand(input, errorStream);
+ REQUIRE(command.type() == typeid(cd_));
+ REQUIRE(boost::get<cd_>(command) == expected);
}
SECTION("invalid input")
{
diff --git a/tests/presence_containers.cpp b/tests/presence_containers.cpp
new file mode 100644
index 0000000..a4050f8
--- /dev/null
+++ b/tests/presence_containers.cpp
@@ -0,0 +1,93 @@
+
+/*
+ * Copyright (C) 2018 CESNET, https://photonics.cesnet.cz/
+ * Copyright (C) 2018 FIT CVUT, https://fit.cvut.cz/
+ *
+ * Written by Václav Kubernát <kubervac@fit.cvut.cz>
+ *
+*/
+
+#include "trompeloeil_catch.h"
+#include "ast.hpp"
+#include "parser.hpp"
+#include "schema.hpp"
+
+TEST_CASE("presence containers")
+{
+ Schema schema;
+ schema.addContainer("", "a", yang::ContainerTraits::Presence);
+ schema.addContainer("", "b");
+ schema.addContainer("a", "a2");
+ schema.addContainer("a/a2", "a3", yang::ContainerTraits::Presence);
+ schema.addContainer("b", "b2", yang::ContainerTraits::Presence);
+ schema.addList("", "list", {"quote"});
+ schema.addContainer("list", "contInList", yang::ContainerTraits::Presence);
+ Parser parser(schema);
+ std::string input;
+ std::ostringstream errorStream;
+
+ SECTION("valid input")
+ {
+ path_ expectedPath;
+
+ SECTION("a")
+ {
+ input = "a";
+ expectedPath.m_nodes = { container_("a") };
+ }
+
+ SECTION("b/b2")
+ {
+ input = "b/b2";
+ expectedPath.m_nodes = { container_("b"), container_("b2") };
+ }
+
+ SECTION("a/a2/a3")
+ {
+ input = "a/a2/a3";
+ expectedPath.m_nodes = { container_("a"), container_("a2"), container_("a3") };
+ }
+
+ SECTION("list[quote=lol]/contInList")
+ {
+ input = "list[quote=lol]/contInList";
+ auto keys = std::map<std::string, std::string>{
+ {"quote", "lol"}};
+ expectedPath.m_nodes = { listElement_("list", keys), container_("contInList") };
+ }
+
+ create_ expectedCreate;
+ expectedCreate.m_path = expectedPath;
+ command_ commandCreate = parser.parseCommand("create " + input, errorStream);
+ REQUIRE(commandCreate.type() == typeid(create_));
+ create_ create = boost::get<create_>(commandCreate);
+ REQUIRE(create == expectedCreate);
+
+ delete_ expectedDelete;
+ expectedDelete.m_path = expectedPath;
+ command_ commandDelete = parser.parseCommand("delete " + input, errorStream);
+ REQUIRE(commandDelete.type() == typeid(delete_));
+ delete_ delet = boost::get<delete_>(commandDelete);
+ REQUIRE(delet == expectedDelete);
+ }
+ SECTION("invalid input")
+ {
+ SECTION("c")
+ {
+ input = "c";
+ }
+
+ SECTION("a/a2")
+ {
+ input = "a/a2";
+ }
+
+ SECTION("list[quote=lol]")
+ {
+ input = "list[quote=lol]";
+ }
+
+ REQUIRE_THROWS(parser.parseCommand("create " + input, errorStream));
+ REQUIRE_THROWS(parser.parseCommand("delete " + input, errorStream));
+ }
+}