Add support for leafrefs

Change-Id: I2286aaa96abd960d13bb0b04a778fb264b2f74a4
diff --git a/src/ast_handlers.hpp b/src/ast_handlers.hpp
index 9fd65ff..5501247 100644
--- a/src/ast_handlers.hpp
+++ b/src/ast_handlers.hpp
@@ -428,7 +428,11 @@
         leaf_ leaf = boost::get<leaf_>(parserContext.m_curPath.m_nodes.back().m_suffix);
         schemaPath_ location = pathWithoutLastNode(parserContext.m_curPath);
 
-        if (schema.leafType(location, {module, leaf.m_name}) != m_type) {
+        auto type = schema.leafType(location, {module, leaf.m_name});
+        if (type == yang::LeafDataTypes::LeafRef) {
+            type = schema.leafrefBase(location, {module, leaf.m_name});
+        }
+        if (type != m_type) {
             _pass(context) = false;
         }
     }
diff --git a/src/schema.hpp b/src/schema.hpp
index 64f743c..e0d30af 100644
--- a/src/schema.hpp
+++ b/src/schema.hpp
@@ -33,6 +33,7 @@
     Enum,
     Binary,
     IdentityRef,
+    LeafRef,
 };
 }
 
@@ -73,6 +74,8 @@
     virtual bool listHasKey(const schemaPath_& location, const ModuleNodePair& node, const std::string& key) const = 0;
     virtual const std::set<std::string> listKeys(const schemaPath_& location, const ModuleNodePair& node) const = 0;
     virtual yang::LeafDataTypes leafType(const schemaPath_& location, const ModuleNodePair& node) const = 0;
+    virtual yang::LeafDataTypes leafrefBase(const schemaPath_& location, const ModuleNodePair& node) const = 0;
+
     virtual const std::set<std::string> validIdentities(const schemaPath_& location, const ModuleNodePair& node, const Prefixes prefixes) const = 0;
     virtual const std::set<std::string> enumValues(const schemaPath_& location, const ModuleNodePair& node) const = 0;
     virtual std::set<std::string> childNodes(const schemaPath_& path, const Recursion recursion) const = 0;
diff --git a/src/static_schema.cpp b/src/static_schema.cpp
index 280f0da..3c3fced 100644
--- a/src/static_schema.cpp
+++ b/src/static_schema.cpp
@@ -105,7 +105,7 @@
 
 void StaticSchema::addLeaf(const std::string& location, const std::string& name, const yang::LeafDataTypes& type)
 {
-    m_nodes.at(location).emplace(name, yang::leaf{type, {}, {}});
+    m_nodes.at(location).emplace(name, yang::leaf{type, {}, {}, {}});
 }
 
 void StaticSchema::addLeafEnum(const std::string& location, const std::string& name, std::set<std::string> enumValues)
@@ -125,6 +125,14 @@
     m_nodes.at(location).emplace(name, toAdd);
 }
 
+void StaticSchema::addLeafRef(const std::string& location, const std::string& name, const std::string& source)
+{
+    yang::leaf toAdd;
+    toAdd.m_type = yang::LeafDataTypes::LeafRef;
+    toAdd.m_leafRefSource = source;
+    m_nodes.at(location).emplace(name, toAdd);
+}
+
 void StaticSchema::addModule(const std::string& name)
 {
     m_modules.emplace(name);
@@ -198,6 +206,24 @@
     return children(locationString).at(fullName).type() == typeid(yang::leaf);
 }
 
+std::string lastNodeOfSchemaPath(const std::string& path)
+{
+    std::string res = path;
+    auto pos = res.find_last_of('/');
+    if (pos != res.npos)
+        res.erase(0, pos);
+    return res;
+}
+
+yang::LeafDataTypes StaticSchema::leafrefBase(const schemaPath_& location, const ModuleNodePair& node) const
+{
+    std::string locationString = pathToAbsoluteSchemaString(location);
+    auto leaf{boost::get<yang::leaf>(children(locationString).at(fullNodeName(location, node)))};
+    auto locationOfSource = stripLastNodeFromPath(leaf.m_leafRefSource);
+    auto nameOfSource = lastNodeOfSchemaPath(leaf.m_leafRefSource);
+    return boost::get<yang::leaf>(children(locationOfSource).at(nameOfSource)).m_type;
+}
+
 yang::LeafDataTypes StaticSchema::leafType(const schemaPath_& location, const ModuleNodePair& node) const
 {
     std::string locationString = pathToAbsoluteSchemaString(location);
diff --git a/src/static_schema.hpp b/src/static_schema.hpp
index 4af6793..559be6c 100644
--- a/src/static_schema.hpp
+++ b/src/static_schema.hpp
@@ -30,6 +30,7 @@
     yang::LeafDataTypes m_type;
     std::set<std::string> m_enumValues;
     ModuleValuePair m_identBase;
+    std::string m_leafRefSource;
 };
 
 struct module {
@@ -58,6 +59,7 @@
     bool listHasKey(const schemaPath_& location, const ModuleNodePair& node, const std::string& key) const override;
     const std::set<std::string> listKeys(const schemaPath_& location, const ModuleNodePair& node) const override;
     yang::LeafDataTypes leafType(const schemaPath_& location, const ModuleNodePair& node) const override;
+    yang::LeafDataTypes leafrefBase(const schemaPath_& location, const ModuleNodePair& node) const override;
     const std::set<std::string> enumValues(const schemaPath_& location, const ModuleNodePair& node) const override;
     const std::set<std::string> validIdentities(const schemaPath_& location, const ModuleNodePair& node, const Prefixes prefixes) const override;
     std::set<std::string> childNodes(const schemaPath_& path, const Recursion) const override;
@@ -66,6 +68,7 @@
     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);
     void addLeafIdentityRef(const std::string& location, const std::string& name, const ModuleValuePair& base);
+    void addLeafRef(const std::string& location, const std::string& name, const std::string& source);
     void addList(const std::string& location, const std::string& name, const std::set<std::string>& keys);
     void addModule(const std::string& name);
     void addIdentity(const std::optional<ModuleValuePair>& base, const ModuleValuePair& name);
diff --git a/src/utils.cpp b/src/utils.cpp
index d06d35b..979bda4 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -62,6 +62,8 @@
         return "an enum";
     case yang::LeafDataTypes::IdentityRef:
         return "an identity";
+    case yang::LeafDataTypes::LeafRef:
+        return "a leafref";
     default:
         throw std::runtime_error("leafDataTypeToString: unsupported leaf data type");
     }
diff --git a/src/yang_schema.cpp b/src/yang_schema.cpp
index 5f8a8cf..629aa2b 100644
--- a/src/yang_schema.cpp
+++ b/src/yang_schema.cpp
@@ -233,14 +233,9 @@
     return keys;
 }
 
-yang::LeafDataTypes YangSchema::leafType(const schemaPath_& location, const ModuleNodePair& node) const
+yang::LeafDataTypes lyTypeToLeafDataTypes(LY_DATA_TYPE type)
 {
-    using namespace std::string_literals;
-    if (!isLeaf(location, node))
-        throw InvalidSchemaQueryException(fullNodeName(location, node) + " is not a leaf");
-
-    libyang::Schema_Node_Leaf leaf(getSchemaNode(location, node));
-    switch (leaf.type()->base()) {
+    switch (type) {
     case LY_TYPE_STRING:
         return yang::LeafDataTypes::String;
     case LY_TYPE_DEC64:
@@ -269,8 +264,38 @@
         return yang::LeafDataTypes::Binary;
     case LY_TYPE_IDENT:
         return yang::LeafDataTypes::IdentityRef;
+    case LY_TYPE_LEAFREF:
+        return yang::LeafDataTypes::LeafRef;
     default:
-        throw UnsupportedYangTypeException("the type of "s + fullNodeName(location, node) + " is not supported");
+        using namespace std::string_literals;
+        throw std::logic_error{"internal error: unsupported libyang data type "s + std::to_string(type)};
+    }
+
+}
+
+yang::LeafDataTypes YangSchema::leafType(const schemaPath_& location, const ModuleNodePair& node) const
+{
+    using namespace std::string_literals;
+    if (!isLeaf(location, node))
+        throw InvalidSchemaQueryException(fullNodeName(location, node) + " is not a leaf");
+
+    libyang::Schema_Node_Leaf leaf(getSchemaNode(location, node));
+    auto baseType{leaf.type()->base()};
+    try {
+        return lyTypeToLeafDataTypes(baseType);
+    } catch(std::logic_error& ex) {
+        throw UnsupportedYangTypeException("the type of "s + fullNodeName(location, node) + " is not supported: " + ex.what());
+    }
+}
+
+yang::LeafDataTypes YangSchema::leafrefBase(const schemaPath_& location, const ModuleNodePair & node) const
+{
+    using namespace std::string_literals;
+    libyang::Schema_Node_Leaf leaf(getSchemaNode(location, node));
+    try {
+        return lyTypeToLeafDataTypes(leaf.type()->info()->lref()->target()->type()->base());
+    } catch(std::logic_error& ex) {
+        throw UnsupportedYangTypeException("the type of "s + fullNodeName(location, node) + " is not supported: " + ex.what());
     }
 }
 
diff --git a/src/yang_schema.hpp b/src/yang_schema.hpp
index 3621c17..fb4b922 100644
--- a/src/yang_schema.hpp
+++ b/src/yang_schema.hpp
@@ -40,6 +40,7 @@
     bool listHasKey(const schemaPath_& location, const ModuleNodePair& node, const std::string& key) const override;
     const std::set<std::string> listKeys(const schemaPath_& location, const ModuleNodePair& node) const override;
     yang::LeafDataTypes leafType(const schemaPath_& location, const ModuleNodePair& node) const override;
+    yang::LeafDataTypes leafrefBase(const schemaPath_& location, const ModuleNodePair& node) const override;
     const std::set<std::string> validIdentities(const schemaPath_& location, const ModuleNodePair& node, const Prefixes prefixes) const override;
     const std::set<std::string> enumValues(const schemaPath_& location, const ModuleNodePair& node) const override;
     std::set<std::string> childNodes(const schemaPath_& path, const Recursion recursion) const override;
diff --git a/tests/leaf_editing.cpp b/tests/leaf_editing.cpp
index b0db594..bcf06aa 100644
--- a/tests/leaf_editing.cpp
+++ b/tests/leaf_editing.cpp
@@ -48,6 +48,8 @@
     schema->addLeaf("mod:contA", "mod:leafInCont", yang::LeafDataTypes::String);
     schema->addList("", "mod:list", {"number"});
     schema->addLeaf("mod:list", "mod:leafInList", yang::LeafDataTypes::String);
+    schema->addLeafRef("", "mod:refToString", "mod:leafString");
+    schema->addLeafRef("", "mod:refToInt8", "mod:leafInt8");
     Parser parser(schema);
     std::string input;
     std::ostringstream errorStream;
@@ -271,6 +273,22 @@
                     }
                 }
             }
+            SECTION("leafRef")
+            {
+                SECTION("refToString")
+                {
+                    input = "set mod:refToString blabal";
+                    expected.m_path.m_nodes.push_back(dataNode_{module_{"mod"}, leaf_("refToString")});
+                    expected.m_data = std::string("blabal");
+                }
+
+                SECTION("refToInt8")
+                {
+                    input = "set mod:refToInt8 42";
+                    expected.m_path.m_nodes.push_back(dataNode_{module_{"mod"}, leaf_("refToInt8")});
+                    expected.m_data = int8_t{42};
+                }
+            }
         }
 
         command_ command = parser.parseCommand(input, errorStream);
@@ -359,6 +377,10 @@
             {
                 input = "set leafEnum blabla";
             }
+            SECTION("set mod:refToInt8 blabla")
+            {
+                input = "set mod:refToInt8 blabla";
+            }
         }
 
         SECTION("wrong base64 strings")