add presence containers

Change-Id: Ic8e33d68e496deae9dfe4c3e5ebcecbd45ee31b2
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;