Add types for leaves

Change-Id: Ibea2945920e523d2f3439eed2c12a5d719d178f1
diff --git a/src/ast_commands.cpp b/src/ast_commands.cpp
index a8b481d..9ef9044 100644
--- a/src/ast_commands.cpp
+++ b/src/ast_commands.cpp
@@ -7,6 +7,12 @@
 */
 #include "ast_commands.hpp"
 
+enum_::enum_() = default;
+
+enum_::enum_(const std::string& value)
+    : m_value(value)
+{
+}
 
 bool set_::operator==(const set_& b) const
 {
@@ -18,6 +24,11 @@
     return this->m_path == b.m_path;
 }
 
+bool enum_::operator==(const enum_& b) const
+{
+    return this->m_value == b.m_value;
+}
+
 bool create_::operator==(const create_& b) const
 {
     return this->m_path == b.m_path;
diff --git a/src/ast_commands.hpp b/src/ast_commands.hpp
index 5caeb2e..90af8ea 100644
--- a/src/ast_commands.hpp
+++ b/src/ast_commands.hpp
@@ -17,9 +17,12 @@
 using x3::alnum;
 using x3::alpha;
 using x3::char_;
+using x3::double_;
 using x3::expect;
+using x3::int_;
 using x3::lexeme;
 using x3::lit;
+using x3::uint_;
 
 struct parser_context_tag;
 
@@ -40,10 +43,24 @@
     path_ m_path;
 };
 
+struct enum_ {
+    enum_();
+    enum_(const std::string& value);
+    bool operator==(const enum_& b) const;
+    std::string m_value;
+};
+
+using leaf_data_ = boost::variant<enum_,
+                                  double,
+                                  bool,
+                                  int32_t,
+                                  uint32_t,
+                                  std::string>;
+
 struct set_ : x3::position_tagged {
     bool operator==(const set_& b) const;
     path_ m_path;
-    std::string m_data;
+    leaf_data_ m_data;
 };
 
 using command_ = boost::variant<cd_, create_, delete_, set_>;
@@ -51,4 +68,5 @@
 BOOST_FUSION_ADAPT_STRUCT(cd_, m_path)
 BOOST_FUSION_ADAPT_STRUCT(create_, m_path)
 BOOST_FUSION_ADAPT_STRUCT(delete_, m_path)
+BOOST_FUSION_ADAPT_STRUCT(enum_, m_value)
 BOOST_FUSION_ADAPT_STRUCT(set_, m_path, m_data)
diff --git a/src/ast_handlers.hpp b/src/ast_handlers.hpp
index 0d3159b..848ce21 100644
--- a/src/ast_handlers.hpp
+++ b/src/ast_handlers.hpp
@@ -10,6 +10,7 @@
 
 #include "parser_context.hpp"
 #include "schema.hpp"
+#include "utils.hpp"
 
 struct keyValue_class {
     template <typename T, typename Iterator, typename Context>
@@ -90,7 +91,6 @@
         if (parserContext.m_errorMsg.empty())
             parserContext.m_errorMsg = "Expecting ']' here:";
         return x3::error_handler_result::rethrow;
-
     }
 };
 struct listElement_class {
@@ -110,7 +110,6 @@
         } else {
             return x3::error_handler_result::rethrow;
         }
-
     }
 };
 
@@ -177,9 +176,6 @@
     }
 };
 
-struct data_string_class {
-};
-
 struct cd_class {
     template <typename T, typename Iterator, typename Context>
     void on_success(Iterator const&, Iterator const&, T&, Context const&)
@@ -204,8 +200,7 @@
         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)};
+            path_ location = pathWithoutLastNode(parserContext.m_curPath);
 
             if (!schema.isPresenceContainer(location, cont.m_name)) {
                 parserContext.m_errorMsg = "This container is not a presence container.";
@@ -233,6 +228,89 @@
 struct delete_class : public presenceContainerPathHandler {
 };
 
+struct leaf_data_class {
+};
+
+struct leaf_data_base_class {
+    yang::LeafDataTypes m_type;
+
+    leaf_data_base_class(yang::LeafDataTypes type)
+        : m_type(type)
+    {
+    }
+
+    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);
+        auto& schema = parserContext.m_schema;
+
+        leaf_ leaf = boost::get<leaf_>(parserContext.m_curPath.m_nodes.back());
+        path_ location = pathWithoutLastNode(parserContext.m_curPath);
+
+        if (schema.leafType(location, leaf.m_name) != m_type) {
+            _pass(context) = false;
+        }
+    }
+};
+
+struct leaf_data_enum_class : leaf_data_base_class {
+    leaf_data_enum_class()
+        : leaf_data_base_class(yang::LeafDataTypes::Enum)
+    {
+    }
+
+    template <typename T, typename Iterator, typename Context>
+    void on_success(Iterator const& start, Iterator const& end, T& ast, Context const& context)
+    {
+        leaf_data_base_class::on_success(start, end, ast, context);
+        auto& parserContext = x3::get<parser_context_tag>(context);
+        auto& schema = parserContext.m_schema;
+
+        leaf_ leaf = boost::get<leaf_>(parserContext.m_curPath.m_nodes.back());
+        path_ location = pathWithoutLastNode(parserContext.m_curPath);
+
+        if (!schema.leafEnumHasValue(location, leaf.m_name, ast.m_value)) {
+            _pass(context) = false;
+        }
+    }
+};
+
+struct leaf_data_decimal_class : leaf_data_base_class {
+    leaf_data_decimal_class()
+        : leaf_data_base_class(yang::LeafDataTypes::Decimal)
+    {
+    }
+};
+
+struct leaf_data_bool_class : leaf_data_base_class {
+    leaf_data_bool_class()
+        : leaf_data_base_class(yang::LeafDataTypes::Bool)
+    {
+    }
+};
+
+struct leaf_data_int_class : leaf_data_base_class {
+    leaf_data_int_class()
+        : leaf_data_base_class(yang::LeafDataTypes::Int)
+    {
+    }
+};
+
+struct leaf_data_uint_class : leaf_data_base_class {
+    leaf_data_uint_class()
+        : leaf_data_base_class(yang::LeafDataTypes::Uint)
+    {
+    }
+};
+
+struct leaf_data_string_class : leaf_data_base_class {
+    leaf_data_string_class()
+        : leaf_data_base_class(yang::LeafDataTypes::String)
+    {
+    }
+};
+
 struct set_class {
     template <typename T, typename Iterator, typename Context>
     void on_success(Iterator const&, Iterator const&, T& ast, Context const& context)
diff --git a/src/ast_path.cpp b/src/ast_path.cpp
index 2d2c600..2734377 100644
--- a/src/ast_path.cpp
+++ b/src/ast_path.cpp
@@ -69,7 +69,7 @@
                 std::experimental::make_ostream_joiner(res, ' '),
                 [] (const auto& it) { return it.first + "=" + it.second; });
         res << "]";
-    return res.str();
+        return res.str();
     }
     std::string operator()(const nodeup_&) const
     {
diff --git a/src/grammars.hpp b/src/grammars.hpp
index ab410c8..443b28d 100644
--- a/src/grammars.hpp
+++ b/src/grammars.hpp
@@ -21,7 +21,15 @@
 x3::rule<container_class, container_> const container = "container";
 x3::rule<leaf_class, leaf_> const leaf = "leaf";
 x3::rule<path_class, path_> const path = "path";
-x3::rule<data_string_class, std::string> const data_string = "data_string";
+
+x3::rule<leaf_data_class, leaf_data_> const leaf_data = "leaf_data";
+x3::rule<leaf_data_enum_class, enum_> const leaf_data_enum = "leaf_data_enum";
+x3::rule<leaf_data_decimal_class, double> const leaf_data_decimal = "leaf_data_decimal";
+x3::rule<leaf_data_bool_class, bool> const leaf_data_bool = "leaf_data_bool";
+x3::rule<leaf_data_int_class, int32_t> const leaf_data_int = "leaf_data_int";
+x3::rule<leaf_data_uint_class, uint32_t> const leaf_data_uint = "leaf_data_uint";
+x3::rule<leaf_data_string_class, std::string> const leaf_data_string = "leaf_data_string";
+
 x3::rule<cd_class, cd_> const cd = "cd";
 x3::rule<set_class, set_> const set = "set";
 x3::rule<create_class, create_> const create = "create";
@@ -64,8 +72,36 @@
 auto const path_def =
         (x3::expect[container | listElement | nodeup | leaf]) % '/';
 
-auto const data_string_def =
-        lexeme[+char_];
+auto const leaf_data_enum_def =
+        +char_;
+auto const leaf_data_decimal_def =
+        double_;
+
+struct bool_symbol_table : x3::symbols<bool> {
+    bool_symbol_table()
+    {
+        add
+            ("true", true)
+            ("false", false);
+    }
+} const bool_rule;
+
+auto const leaf_data_bool_def =
+        bool_rule;
+auto const leaf_data_int_def =
+        int_;
+auto const leaf_data_uint_def =
+        uint_;
+auto const leaf_data_string_def =
+        *char_;
+
+auto const leaf_data_def =
+        leaf_data_enum |
+        leaf_data_decimal |
+        leaf_data_bool |
+        leaf_data_int |
+        leaf_data_uint |
+        leaf_data_string;
 
 auto const space_separator =
         x3::omit[x3::no_skip[space]];
@@ -80,7 +116,7 @@
         lit("delete") >> space_separator > path;
 
 auto const set_def =
-        lit("set") >> space_separator > path > data_string;
+        lit("set") >> space_separator > path > leaf_data;
 
 auto const command_def =
         x3::expect[cd | create | delete_rule | set] >> x3::eoi;
@@ -98,7 +134,13 @@
 BOOST_SPIRIT_DEFINE(container)
 BOOST_SPIRIT_DEFINE(leaf)
 BOOST_SPIRIT_DEFINE(path)
-BOOST_SPIRIT_DEFINE(data_string)
+BOOST_SPIRIT_DEFINE(leaf_data)
+BOOST_SPIRIT_DEFINE(leaf_data_enum)
+BOOST_SPIRIT_DEFINE(leaf_data_decimal)
+BOOST_SPIRIT_DEFINE(leaf_data_bool)
+BOOST_SPIRIT_DEFINE(leaf_data_int)
+BOOST_SPIRIT_DEFINE(leaf_data_uint)
+BOOST_SPIRIT_DEFINE(leaf_data_string)
 BOOST_SPIRIT_DEFINE(set)
 BOOST_SPIRIT_DEFINE(cd)
 BOOST_SPIRIT_DEFINE(create)
diff --git a/src/interpreter.cpp b/src/interpreter.cpp
index 934aac9..1a4e84a 100644
--- a/src/interpreter.cpp
+++ b/src/interpreter.cpp
@@ -9,9 +9,23 @@
 #include <iostream>
 #include "interpreter.hpp"
 
+struct leafDataToString : boost::static_visitor<std::string> {
+    std::string operator()(const enum_& data) const
+    {
+        return data.m_value;
+    }
+    template <typename T>
+    std::string operator()(const T& data) const
+    {
+        std::stringstream stream;
+        stream << data;
+        return stream.str();
+    }
+};
+
 void Interpreter::operator()(const set_& set) const
 {
-    std::cout << "Setting " << pathToDataString(set.m_path) << " to " << set.m_data << std::endl;
+    std::cout << "Setting " << pathToDataString(set.m_path) << " to " << boost::apply_visitor(leafDataToString(), set.m_data) << std::endl;
 }
 
 void Interpreter::operator()(const cd_& cd) const
diff --git a/src/main.cpp b/src/main.cpp
index df4c925..e498db0 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -49,9 +49,14 @@
     Schema schema;
     schema.addContainer("", "a", yang::ContainerTraits::Presence);
     schema.addContainer("", "b");
-    schema.addLeaf("", "leaf");
+    schema.addLeaf("", "leafString", yang::LeafDataTypes::String);
+    schema.addLeaf("", "leafDecimal", yang::LeafDataTypes::Decimal);
+    schema.addLeaf("", "leafBool", yang::LeafDataTypes::Bool);
+    schema.addLeaf("", "leafInt", yang::LeafDataTypes::Int);
+    schema.addLeaf("", "leafUint", yang::LeafDataTypes::Uint);
+    schema.addLeafEnum("", "leafEnum", {"lol", "data", "coze"});
     schema.addContainer("a", "a2");
-    schema.addLeaf("a", "leafa");
+    schema.addLeaf("a", "leafa", yang::LeafDataTypes::String);
     schema.addContainer("b", "b2", yang::ContainerTraits::Presence);
     schema.addContainer("a/a2", "a3", yang::ContainerTraits::Presence);
     schema.addContainer("b/b2", "b3");
diff --git a/src/schema.cpp b/src/schema.cpp
index 95cd4ae..066dc3b 100644
--- a/src/schema.cpp
+++ b/src/schema.cpp
@@ -96,9 +96,24 @@
     return boost::get<yang::container>(children(locationString).at(name)).m_presence == yang::ContainerTraits::Presence;
 }
 
-void Schema::addLeaf(const std::string& location, const std::string& name)
+void Schema::addLeaf(const std::string& location, const std::string& name, const yang::LeafDataTypes& type)
 {
-    m_nodes.at(location).emplace(name, yang::leaf{});
+    m_nodes.at(location).emplace(name, yang::leaf{type, {}});
+}
+
+void Schema::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});
+}
+
+bool Schema::leafEnumHasValue(const path_& location, const std::string& name, const std::string& value) const
+{
+    std::string locationString = pathToSchemaString(location);
+    assert(isLeaf(location, name));
+
+    const auto& child = children(locationString).at(name);
+    const auto& list = boost::get<yang::leaf>(child);
+    return list.m_enumValues.find(value) != list.m_enumValues.end();
 }
 
 bool Schema::isLeaf(const path_& location, const std::string& name) const
@@ -109,3 +124,9 @@
 
     return children(locationString).at(name).type() == typeid(yang::leaf);
 }
+
+yang::LeafDataTypes Schema::leafType(const path_& location, const std::string& name) const
+{
+    std::string locationString = pathToSchemaString(location);
+    return boost::get<yang::leaf>(children(locationString).at(name)).m_type;
+}
diff --git a/src/schema.hpp b/src/schema.hpp
index 73e89df..e87619b 100644
--- a/src/schema.hpp
+++ b/src/schema.hpp
@@ -19,6 +19,16 @@
     Presence,
     None,
 };
+
+enum class LeafDataTypes {
+    String,
+    Decimal,
+    Bool,
+    Int,
+    Uint,
+    Enum,
+};
+
 struct container {
     yang::ContainerTraits m_presence;
 };
@@ -26,6 +36,8 @@
     std::set<std::string> m_keys;
 };
 struct leaf {
+    yang::LeafDataTypes m_type;
+    std::set<std::string> m_enumValues;
 };
 }
 
@@ -54,8 +66,11 @@
     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;
-    void addLeaf(const std::string& location, const std::string& name);
+    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);
+    bool leafEnumHasValue(const path_& location, const std::string& name, const std::string& value) const;
     bool isLeaf(const path_& location, const std::string& name) const;
+    yang::LeafDataTypes leafType(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/src/utils.cpp b/src/utils.cpp
index 77466c4..a7fea46 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -25,3 +25,8 @@
         res.erase(pos);
     return res;
 }
+
+path_ pathWithoutLastNode(const path_& path)
+{
+    return path_{decltype(path_::m_nodes)(path.m_nodes.begin(), path.m_nodes.end() - 1)};
+}
diff --git a/src/utils.hpp b/src/utils.hpp
index 4f10102..2c550d7 100644
--- a/src/utils.hpp
+++ b/src/utils.hpp
@@ -6,6 +6,8 @@
  *
 */
 #include <string>
+#include "ast_path.hpp"
 
 std::string joinPaths(const std::string& prefix, const std::string& suffix);
 std::string stripLastNodeFromPath(const std::string& path);
+path_ pathWithoutLastNode(const path_& path);
diff --git a/tests/leaf_editing.cpp b/tests/leaf_editing.cpp
index 5894921..09e8504 100644
--- a/tests/leaf_editing.cpp
+++ b/tests/leaf_editing.cpp
@@ -15,10 +15,15 @@
 {
     Schema schema;
     schema.addContainer("", "contA");
-    schema.addLeaf("", "leaf");
-    schema.addLeaf("contA", "leafInCont");
+    schema.addLeaf("", "leafString", yang::LeafDataTypes::String);
+    schema.addLeaf("", "leafDecimal", yang::LeafDataTypes::Decimal);
+    schema.addLeaf("", "leafBool", yang::LeafDataTypes::Bool);
+    schema.addLeaf("", "leafInt", yang::LeafDataTypes::Int);
+    schema.addLeaf("", "leafUint", yang::LeafDataTypes::Uint);
+    schema.addLeafEnum("", "leafEnum", {"lol", "data", "coze"});
+    schema.addLeaf("contA", "leafInCont", yang::LeafDataTypes::String);
     schema.addList("", "list", {"number"});
-    schema.addLeaf("list", "leafInList");
+    schema.addLeaf("list", "leafInList", yang::LeafDataTypes::String);
     Parser parser(schema);
     std::string input;
     std::ostringstream errorStream;
@@ -27,11 +32,11 @@
     {
         set_ expected;
 
-        SECTION("set leaf some_data")
+        SECTION("set leafString some_data")
         {
-            input = "set leaf some_data";
-            expected.m_path.m_nodes.push_back(leaf_("leaf"));
-            expected.m_data = "some_data";
+            input = "set leafString some_data";
+            expected.m_path.m_nodes.push_back(leaf_("leafString"));
+            expected.m_data = std::string("some_data");
         }
 
         SECTION("set contA/leafInCont more_data")
@@ -39,7 +44,55 @@
             input = "set contA/leafInCont more_data";
             expected.m_path.m_nodes.push_back(container_("contA"));
             expected.m_path.m_nodes.push_back(leaf_("leafInCont"));
-            expected.m_data = "more_data";
+            expected.m_data = std::string("more_data");
+        }
+
+        SECTION("set list[number=1]/leafInList another_data")
+        {
+            input = "set list[number=1]/leafInList another_data";
+            auto keys = std::map<std::string, std::string>{
+                {"number", "1"}};
+            expected.m_path.m_nodes.push_back(listElement_("list", keys));
+            expected.m_path.m_nodes.push_back(leaf_("leafInList"));
+            expected.m_data = std::string("another_data");
+        }
+
+        SECTION("data types")
+        {
+            SECTION("string")
+            {
+                input = "set leafString somedata";
+                expected.m_path.m_nodes.push_back(leaf_("leafString"));
+                expected.m_data = std::string("somedata");
+            }
+
+            SECTION("int")
+            {
+                input = "set leafInt 2";
+                expected.m_path.m_nodes.push_back(leaf_("leafInt"));
+                expected.m_data = 2;
+            }
+
+            SECTION("decimal")
+            {
+                input = "set leafDecimal 3.14159";
+                expected.m_path.m_nodes.push_back(leaf_("leafDecimal"));
+                expected.m_data = 3.14159;
+            }
+
+            SECTION("enum")
+            {
+                input = "set leafEnum coze";
+                expected.m_path.m_nodes.push_back(leaf_("leafEnum"));
+                expected.m_data = enum_("coze");
+            }
+
+            SECTION("bool")
+            {
+                input = "set leafBool true";
+                expected.m_path.m_nodes.push_back(leaf_("leafBool"));
+                expected.m_data = true;
+            }
         }
 
         command_ command = parser.parseCommand(input, errorStream);
@@ -78,6 +131,26 @@
             }
         }
 
+        SECTION("wrong types")
+        {
+            SECTION("set leafBool blabla")
+            {
+                input = "set leafBool blabla";
+            }
+            SECTION("set leafUint blabla")
+            {
+                input = "set leafUint blabla";
+            }
+            SECTION("set leafInt blabla")
+            {
+                input = "set leafInt blabla";
+            }
+            SECTION("set leafEnum blabla")
+            {
+                input = "set leafEnum blabla";
+            }
+        }
+
         REQUIRE_THROWS(parser.parseCommand(input, errorStream));
     }
 }
diff --git a/tests/presence_containers.cpp b/tests/presence_containers.cpp
index 72c9daf..8958627 100644
--- a/tests/presence_containers.cpp
+++ b/tests/presence_containers.cpp
@@ -33,19 +33,19 @@
         SECTION("a")
         {
             input = "a";
-            expectedPath.m_nodes = { container_("a") };
+            expectedPath.m_nodes = {container_("a")};
         }
 
         SECTION("b/b2")
         {
             input = "b/b2";
-            expectedPath.m_nodes = { container_("b"), container_("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") };
+            expectedPath.m_nodes = {container_("a"), container_("a2"), container_("a3")};
         }
 
         SECTION("list[quote=lol]/contInList")
@@ -53,7 +53,7 @@
             input = "list[quote=lol]/contInList";
             auto keys = std::map<std::string, std::string>{
                 {"quote", "lol"}};
-            expectedPath.m_nodes = { listElement_("list", keys), container_("contInList") };
+            expectedPath.m_nodes = {listElement_("list", keys), container_("contInList")};
         }
 
         create_ expectedCreate;