Fix leafDataFromValue

Leafrefs were not working correctly, when pointing to a data node not in
the tree. Because of this I had to rework the thing a little bit (I
basically copied the algorithm from cla-sysrepo, where I had the same
issue). I also changed the name. And also added test, both versions of
leafref included.

Funnily enough, it seems that I was already fixing this before, but for
some reason the bug appeared again while using netconf-cli, oh well, now
the tests should cover that hopefully.

Change-Id: I8a102c54bf58a4221610c8a83d26979fb7b4461d
diff --git a/src/libyang_utils.cpp b/src/libyang_utils.cpp
index 274fec1..f1af868 100644
--- a/src/libyang_utils.cpp
+++ b/src/libyang_utils.cpp
@@ -4,60 +4,65 @@
 #include "libyang_utils.hpp"
 #include "utils.hpp"
 
-leaf_data_ leafValueFromValue(const libyang::S_Value& value, LY_DATA_TYPE type)
+leaf_data_ leafValueFromNode(libyang::S_Data_Node_Leaf_List node)
 {
-    using namespace std::string_literals;
-    switch (type) {
-    case LY_TYPE_INT8:
-        return value->int8();
-    case LY_TYPE_INT16:
-        return value->int16();
-    case LY_TYPE_INT32:
-        return value->int32();
-    case LY_TYPE_INT64:
-        return value->int64();
-    case LY_TYPE_UINT8:
-        return value->uint8();
-    case LY_TYPE_UINT16:
-        return value->uint16();
-    case LY_TYPE_UINT32:
-        return value->uint32();
-    case LY_TYPE_UINT64:
-        return value->uint64();
-    case LY_TYPE_BOOL:
-        return value->bln();
-    case LY_TYPE_STRING:
-        return std::string(value->string());
-    case LY_TYPE_ENUM:
-        return enum_{std::string(value->enm()->name())};
-    case LY_TYPE_IDENT:
-        return identityRef_{value->ident()->module()->name(), value->ident()->name()};
-    case LY_TYPE_BINARY:
-        return binary_{value->binary()};
-    case LY_TYPE_EMPTY:
-        return empty_{};
-    case LY_TYPE_BITS:
-    {
-        auto bits = value->bit();
-        std::vector<libyang::S_Type_Bit> filterNull;
-        std::copy_if(bits.begin(), bits.end(), std::back_inserter(filterNull), [] (auto bit) { return bit; });
-        bits_ res;
-        std::transform(filterNull.begin(), filterNull.end(), std::inserter(res.m_bits, res.m_bits.end()), [] (const auto& bit) { return bit->name(); });
-        return bits_{res};
+    std::function<leaf_data_(libyang::S_Data_Node_Leaf_List)> impl = [&impl] (libyang::S_Data_Node_Leaf_List node) -> leaf_data_ {
+        // value_type() is what's ACTUALLY stored inside `node`
+        // Leafrefs sometimes don't hold a reference to another, but they have the actual pointed-to value.
+        switch (node->value_type()) {
+        case LY_TYPE_ENUM:
+            return enum_{node->value()->enm()->name()};
+        case LY_TYPE_UINT8:
+            return node->value()->uint8();
+        case LY_TYPE_UINT16:
+            return node->value()->uint16();
+        case LY_TYPE_UINT32:
+            return node->value()->uint32();
+        case LY_TYPE_UINT64:
+            return node->value()->uint64();
+        case LY_TYPE_INT8:
+            return node->value()->int8();
+        case LY_TYPE_INT16:
+            return node->value()->int16();
+        case LY_TYPE_INT32:
+            return node->value()->int32();
+        case LY_TYPE_INT64:
+            return node->value()->int64();
+        case LY_TYPE_DEC64:
+        {
+            auto v = node->value()->dec64();
+            return v.value * std::pow(10, -v.digits);
+        }
+        case LY_TYPE_BOOL:
+            return node->value()->bln();
+        case LY_TYPE_STRING:
+            return std::string{node->value()->string()};
+        case LY_TYPE_BINARY:
+            return binary_{node->value()->binary()};
+        case LY_TYPE_IDENT:
+            return identityRef_{node->value()->ident()->module()->name(), node->value()->ident()->name()};
+        case LY_TYPE_EMPTY:
+            return empty_{};
+        case LY_TYPE_LEAFREF:
+        {
+            auto refsTo = node->value()->leafref();
+            assert(refsTo);
+            return impl(std::make_shared<libyang::Data_Node_Leaf_List>(node->value()->leafref()));
+        }
+        case LY_TYPE_BITS:
+        {
+            auto bits = node->value()->bit();
+            std::vector<libyang::S_Type_Bit> filterNull;
+            std::copy_if(bits.begin(), bits.end(), std::back_inserter(filterNull), [] (auto bit) { return bit; });
+            bits_ res;
+            std::transform(filterNull.begin(), filterNull.end(), std::inserter(res.m_bits, res.m_bits.end()), [] (const auto& bit) { return bit->name(); });
+            return bits_{res};
+        }
+        default:
+            return std::string{"(can't print)"};
     }
-    case LY_TYPE_DEC64:
-    {
-        auto v = value->dec64();
-        return v.value * std::pow(10, -v.digits);
-    }
-    case LY_TYPE_LEAFREF:
-    {
-        libyang::Data_Node_Leaf_List toPrint{value->leafref()};
-        return leafValueFromValue(toPrint.value(), toPrint.value_type());
-    }
-    default: // TODO: implement all types
-        return "(can't print)"s;
-    }
+    };
+    return impl(node);
 }
 
 namespace {
@@ -80,8 +85,8 @@
             res.emplace_back(stripXPathPrefix(it->path()), special_{SpecialValue::List});
         }
         if (it->schema()->nodetype() == LYS_LEAF || it->schema()->nodetype() == LYS_LEAFLIST) {
-            libyang::Data_Node_Leaf_List leaf(it);
-            auto value = leafValueFromValue(leaf.value(), leaf.value_type());
+            auto leaf = std::make_shared<libyang::Data_Node_Leaf_List>(it);
+            auto value = leafValueFromNode(leaf);
             res.emplace_back(stripXPathPrefix(it->path()), value);
         }
     }