Add support for identityref data type

Change-Id: I369150b83d86a4c22fadf383ee011eee619a4c15
diff --git a/src/ast_handlers.hpp b/src/ast_handlers.hpp
index d365641..fede04c 100644
--- a/src/ast_handlers.hpp
+++ b/src/ast_handlers.hpp
@@ -360,6 +360,22 @@
     }
 };
 
+// This handler only checks if the module exists
+// It doesn't set any ParserContext flags (except the error message)
+struct data_module_prefix_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);
+        const auto& schema = parserContext.m_schema;
+
+        if (!schema.isModule(parserContext.m_curPath, ast.m_name)) {
+            parserContext.m_errorMsg = "Invalid module name.";
+            _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)
@@ -369,10 +385,10 @@
         if (parserContext.m_errorMsg.empty()) {
             leaf_ leaf = boost::get<leaf_>(parserContext.m_curPath.m_nodes.back().m_suffix);
             schemaPath_ location = pathWithoutLastNode(parserContext.m_curPath);
-            if (location.m_nodes.empty()) {
-                parserContext.m_curModule = parserContext.m_curPath.m_nodes.back().m_prefix->m_name;
-            }
-            parserContext.m_errorMsg = "Expected " + leafDataTypeToString(schema.leafType(location, {parserContext.m_curModule, leaf.m_name})) + " here:";
+            boost::optional<std::string> module;
+            if (parserContext.m_curPath.m_nodes.back().m_prefix)
+                module = parserContext.m_curPath.m_nodes.back().m_prefix.value().m_name;
+            parserContext.m_errorMsg = "Expected " + leafDataTypeToString(schema.leafType(location, {module, leaf.m_name})) + " here:";
             return x3::error_handler_result::fail;
         }
         return x3::error_handler_result::rethrow;
@@ -415,6 +431,9 @@
     void on_success(Iterator const& start, Iterator const& end, T& ast, Context const& context)
     {
         leaf_data_base_class::on_success(start, end, ast, context);
+        // Base class on_success cannot return for us, so we check if it failed the parser.
+        if (_pass(context) == false)
+            return;
         auto& parserContext = x3::get<parser_context_tag>(context);
         auto& schema = parserContext.m_schema;
         boost::optional<std::string> module;
@@ -474,6 +493,43 @@
     }
 };
 
+struct leaf_data_identityRef_data_class;
+
+struct leaf_data_identityRef_class : leaf_data_base_class {
+    leaf_data_identityRef_class()
+        : leaf_data_base_class(yang::LeafDataTypes::IdentityRef)
+    {
+    }
+
+    template <typename T, typename Iterator, typename Context>
+    void on_success(Iterator const& start, Iterator const& end, T& ast, Context const& context)
+    {
+        // FIXME: can I reuse leaf_data_enum_class somehow..?
+        leaf_data_base_class::on_success(start, end, ast, context);
+        // Base class on_success cannot return for us, so we check if it failed the parser.
+        if (_pass(context) == false)
+            return;
+        auto& parserContext = x3::get<parser_context_tag>(context);
+        auto& schema = parserContext.m_schema;
+        boost::optional<std::string> module;
+        if (parserContext.m_curPath.m_nodes.back().m_prefix)
+            module = parserContext.m_curPath.m_nodes.back().m_prefix.value().m_name;
+
+        leaf_ leaf = boost::get<leaf_>(parserContext.m_curPath.m_nodes.back().m_suffix);
+        schemaPath_ location = pathWithoutLastNode(parserContext.m_curPath);
+
+        ModuleValuePair pair;
+        if (ast.m_prefix) {
+            pair.first = ast.m_prefix.get().m_name;
+        }
+        pair.second = ast.m_value;
+
+        if (!schema.leafIdentityIsValid(location, {module, leaf.m_name}, pair)) {
+            _pass(context) = false;
+        }
+    }
+};
+
 struct set_class {
     template <typename Iterator, typename Exception, typename Context>
     x3::error_handler_result on_error(Iterator&, Iterator const&, Exception const& x, Context const& context)
@@ -616,6 +672,32 @@
         leaf_ leaf = boost::get<leaf_>(parserContext.m_curPath.m_nodes.back().m_suffix);
         schemaPath_ location = pathWithoutLastNode(parserContext.m_curPath);
 
-        parserContext.m_suggestions = schema.enumValues(location, {module, leaf.m_name});
+        // Only generate completions if the type is enum so that we don't
+        // overwrite some other completions.
+        if (schema.leafType(location, {module, leaf.m_name}) == yang::LeafDataTypes::Enum)
+            parserContext.m_suggestions = schema.enumValues(location, {module, leaf.m_name});
+    }
+};
+
+// FIXME: can I reuse createEnumSuggestions?
+struct createIdentitySuggestions_class {
+    template <typename T, typename Iterator, typename Context>
+    void on_success(Iterator const& begin, Iterator const&, T&, Context const& context)
+    {
+        auto& parserContext = x3::get<parser_context_tag>(context);
+        parserContext.m_completionIterator = begin;
+        const Schema& schema = parserContext.m_schema;
+
+        boost::optional<std::string> module;
+        if (parserContext.m_curPath.m_nodes.back().m_prefix)
+            module = parserContext.m_curPath.m_nodes.back().m_prefix.value().m_name;
+
+        leaf_ leaf = boost::get<leaf_>(parserContext.m_curPath.m_nodes.back().m_suffix);
+        schemaPath_ location = pathWithoutLastNode(parserContext.m_curPath);
+
+        // Only generate completions if the type is identityref so that we
+        // don't overwrite some other completions.
+        if (schema.leafType(location, {module, leaf.m_name}) == yang::LeafDataTypes::IdentityRef)
+            parserContext.m_suggestions = schema.validIdentities(location, {module, leaf.m_name}, Prefixes::WhenNeeded);
     }
 };