Make set report the expected type in case of error

Change-Id: Id46251b4859739e3c8f77d3bf2513ec22b048cf1
diff --git a/src/ast_handlers.hpp b/src/ast_handlers.hpp
index 848ce21..65d8405 100644
--- a/src/ast_handlers.hpp
+++ b/src/ast_handlers.hpp
@@ -228,7 +228,34 @@
 struct delete_class : public presenceContainerPathHandler {
 };
 
+struct leaf_path_class {
+    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);
+        try {
+            auto leaf = boost::get<leaf_>(ast.m_nodes.back());
+        } catch (boost::bad_get&) {
+            parserContext.m_errorMsg = "This is not a path to leaf.";
+            _pass(context) = false;
+        }
+    }
+};
+
 struct leaf_data_class {
+    template <typename Iterator, typename Exception, typename Context>
+    x3::error_handler_result on_error(Iterator&, Iterator const&, Exception const&, Context const& context)
+    {
+        auto& parserContext = x3::get<parser_context_tag>(context);
+        auto& schema = parserContext.m_schema;
+        if (parserContext.m_errorMsg.empty()) {
+            leaf_ leaf = boost::get<leaf_>(parserContext.m_curPath.m_nodes.back());
+            path_ location = pathWithoutLastNode(parserContext.m_curPath);
+            parserContext.m_errorMsg = "Expected " + leafDataTypeToString(schema.leafType(location, leaf.m_name)) + " here:";
+            return x3::error_handler_result::fail;
+        }
+        return x3::error_handler_result::rethrow;
+    }
 };
 
 struct leaf_data_base_class {
@@ -312,18 +339,6 @@
 };
 
 struct set_class {
-    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);
-        try {
-            auto leaf = boost::get<leaf_>(ast.m_path.m_nodes.back());
-        } catch (boost::bad_get&) {
-            parserContext.m_errorMsg = "This is not a leaf.";
-            _pass(context) = false;
-        }
-    }
-
     template <typename Iterator, typename Exception, typename Context>
     x3::error_handler_result on_error(Iterator&, Iterator const&, Exception const& x, Context const& context)
     {
diff --git a/src/grammars.hpp b/src/grammars.hpp
index 443b28d..5386417 100644
--- a/src/grammars.hpp
+++ b/src/grammars.hpp
@@ -21,6 +21,7 @@
 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<leaf_path_class, path_> const leafPath = "leafPath";
 
 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";
@@ -72,6 +73,9 @@
 auto const path_def =
         (x3::expect[container | listElement | nodeup | leaf]) % '/';
 
+auto const leafPath_def =
+        path;
+
 auto const leaf_data_enum_def =
         +char_;
 auto const leaf_data_decimal_def =
@@ -96,12 +100,13 @@
         *char_;
 
 auto const leaf_data_def =
+x3::expect[
         leaf_data_enum |
         leaf_data_decimal |
         leaf_data_bool |
         leaf_data_int |
         leaf_data_uint |
-        leaf_data_string;
+        leaf_data_string];
 
 auto const space_separator =
         x3::omit[x3::no_skip[space]];
@@ -116,7 +121,7 @@
         lit("delete") >> space_separator > path;
 
 auto const set_def =
-        lit("set") >> space_separator > path > leaf_data;
+        lit("set") >> space_separator > leafPath > leaf_data;
 
 auto const command_def =
         x3::expect[cd | create | delete_rule | set] >> x3::eoi;
@@ -133,6 +138,7 @@
 BOOST_SPIRIT_DEFINE(nodeup)
 BOOST_SPIRIT_DEFINE(container)
 BOOST_SPIRIT_DEFINE(leaf)
+BOOST_SPIRIT_DEFINE(leafPath)
 BOOST_SPIRIT_DEFINE(path)
 BOOST_SPIRIT_DEFINE(leaf_data)
 BOOST_SPIRIT_DEFINE(leaf_data_enum)
diff --git a/src/utils.cpp b/src/utils.cpp
index a7fea46..b4f60ab 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -30,3 +30,23 @@
 {
     return path_{decltype(path_::m_nodes)(path.m_nodes.begin(), path.m_nodes.end() - 1)};
 }
+
+std::string leafDataTypeToString(yang::LeafDataTypes type)
+{
+    switch (type) {
+    case yang::LeafDataTypes::String:
+        return "a string";
+    case yang::LeafDataTypes::Decimal:
+        return "a decimal";
+    case yang::LeafDataTypes::Bool:
+        return "a boolean";
+    case yang::LeafDataTypes::Int:
+        return "an integer";
+    case yang::LeafDataTypes::Uint:
+        return "an unsigned integer";
+    case yang::LeafDataTypes::Enum:
+        return "an enum";
+    default:
+        return "";
+    }
+}
diff --git a/src/utils.hpp b/src/utils.hpp
index 2c550d7..9583ebe 100644
--- a/src/utils.hpp
+++ b/src/utils.hpp
@@ -7,7 +7,9 @@
 */
 #include <string>
 #include "ast_path.hpp"
+#include "schema.hpp"
 
 std::string joinPaths(const std::string& prefix, const std::string& suffix);
 std::string stripLastNodeFromPath(const std::string& path);
 path_ pathWithoutLastNode(const path_& path);
+std::string leafDataTypeToString(yang::LeafDataTypes type);