Migrate to libyang2

libnetconf2: getSchema and getConfig were no longer used in netconf-cli,
so I deleted them. They can get readded once the bindings get split into
a separate project.

sysrepo_access: Some sr_val stuff was removed.

YangSchema: type descriptions are not available
            availableNodes returns only input nodes for RPC nodes
            impl_getSchemaNode: no longer disables error printing

libyang: No longer supports leafrefs without the leaf it points to.

Depends-on: https://cesnet-gerrit-czechlight/c/CzechLight/dependencies/+/5171
Depends-on: https://gerrit.cesnet.cz/c/CzechLight/dependencies/+/5171
Change-Id: Ie49381a003a61a7bb028be7b2fa1d9d926ac4e58
diff --git a/src/libyang_utils.cpp b/src/libyang_utils.cpp
index a6b22cd..f3881fe 100644
--- a/src/libyang_utils.cpp
+++ b/src/libyang_utils.cpp
@@ -1,130 +1,131 @@
 #include <boost/algorithm/string/predicate.hpp>
 #include <cmath>
+#include <libyang-cpp/Context.hpp>
 #include "datastore_access.hpp"
 #include "libyang_utils.hpp"
 #include "utils.hpp"
 
-leaf_data_ leafValueFromNode(libyang::S_Data_Node_Leaf_List node)
+struct impl_leafValueFromNode {
+    leaf_data_ operator()(const libyang::Empty) const
+    {
+        return empty_{};
+    }
+
+    leaf_data_ operator()(const libyang::Binary& bin) const
+    {
+        return binary_{std::string{bin.base64}};
+    }
+
+    leaf_data_ operator()(const std::vector<libyang::Bit>& bits) const
+    {
+        bits_ res;
+        std::transform(bits.begin(), bits.end(), std::back_inserter(res.m_bits), [] (const libyang::Bit& bit) {
+            return bit.name;
+        });
+        return res;
+    }
+
+    leaf_data_ operator()(const libyang::Enum& enumVal) const
+    {
+        return enum_{enumVal.name};
+    }
+
+    leaf_data_ operator()(const libyang::IdentityRef& identRef) const
+    {
+        return identityRef_{identRef.module, identRef.name};
+    }
+
+    leaf_data_ operator()(const libyang::Decimal64& dec) const
+    {
+        return dec.number * std::pow(10, -dec.digits);
+    }
+
+    leaf_data_ operator()(const std::optional<libyang::DataNode>&) const
+    {
+        throw std::runtime_error("instance-identifier is not supported");
+    }
+
+    template <typename Type>
+    leaf_data_ operator()(const Type& val) const
+    {
+        return val;
+    }
+};
+
+leaf_data_ leafValueFromNode(libyang::DataNodeTerm node)
 {
-    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)"};
-        }
-    };
-    return impl(node);
+    return std::visit(impl_leafValueFromNode{},node.value());
 }
 
 namespace {
-void impl_lyNodesToTree(DatastoreAccess::Tree& res, const std::vector<std::shared_ptr<libyang::Data_Node>> items, std::optional<std::string> ignoredXPathPrefix)
+template <typename CollectionType>
+void impl_lyNodesToTree(DatastoreAccess::Tree& res, CollectionType items, std::optional<std::string> ignoredXPathPrefix)
 {
     auto stripXPathPrefix = [&ignoredXPathPrefix](auto path) {
         return ignoredXPathPrefix && path.find(*ignoredXPathPrefix) != std::string::npos ? path.substr(ignoredXPathPrefix->size()) : path;
     };
 
     for (const auto& it : items) {
-        if (it->schema()->nodetype() == LYS_CONTAINER) {
-            if (libyang::Schema_Node_Container{it->schema()}.presence()) {
+        if (it.schema().nodeType() == libyang::NodeType::Container) {
+            if (it.schema().asContainer().isPresence()) {
                 // The fact that the container is included in the data tree
                 // means that it is present and I don't need to check any
                 // value.
-                res.emplace_back(stripXPathPrefix(it->path()), special_{SpecialValue::PresenceContainer});
+                res.emplace_back(stripXPathPrefix(std::string{it.path()}), special_{SpecialValue::PresenceContainer});
             }
         }
-        if (it->schema()->nodetype() == LYS_LIST) {
-            res.emplace_back(stripXPathPrefix(it->path()), special_{SpecialValue::List});
+        if (it.schema().nodeType() == libyang::NodeType::List) {
+            res.emplace_back(stripXPathPrefix(std::string{it.path()}), special_{SpecialValue::List});
         }
-        if (it->schema()->nodetype() == LYS_LEAF || it->schema()->nodetype() == LYS_LEAFLIST) {
-            auto leaf = std::make_shared<libyang::Data_Node_Leaf_List>(it);
-            auto value = leafValueFromNode(leaf);
-            res.emplace_back(stripXPathPrefix(it->path()), value);
+        if (it.schema().nodeType() == libyang::NodeType::Leaf || it.schema().nodeType() == libyang::NodeType::Leaflist) {
+            auto term = it.asTerm();
+            auto value = leafValueFromNode(term);
+            res.emplace_back(stripXPathPrefix(std::string{it.path()}), value);
         }
     }
 }
 }
 
-void lyNodesToTree(DatastoreAccess::Tree& res, const std::vector<std::shared_ptr<libyang::Data_Node>> items, std::optional<std::string> ignoredXPathPrefix)
+template <typename CollectionType>
+void lyNodesToTree(DatastoreAccess::Tree& res, CollectionType items, std::optional<std::string> ignoredXPathPrefix)
 {
-    for (auto it = items.begin(); it < items.end(); it++) {
-        if ((*it)->schema()->nodetype() == LYS_LEAFLIST) {
-            auto leafListPath = stripLeafListValueFromPath((*it)->path());
+    for (auto it = items.begin(); it != items.end(); /* nothing */) {
+        if ((*it).schema().nodeType() == libyang::NodeType::Leaflist) {
+            auto leafListPath = stripLeafListValueFromPath(std::string{(*it).path()});
             res.emplace_back(leafListPath, special_{SpecialValue::LeafList});
-            while (it != items.end() && boost::starts_with((*it)->path(), leafListPath)) {
-                impl_lyNodesToTree(res, (*it)->tree_dfs(), ignoredXPathPrefix);
+            while (it != items.end() && boost::starts_with(std::string{(*it).path()}, leafListPath)) {
+                impl_lyNodesToTree(res, it->childrenDfs(), ignoredXPathPrefix);
                 it++;
             }
         } else {
-            impl_lyNodesToTree(res, (*it)->tree_dfs(), ignoredXPathPrefix);
+            impl_lyNodesToTree(res, it->childrenDfs(), ignoredXPathPrefix);
+            it++;
         }
     }
 }
 
-DatastoreAccess::Tree rpcOutputToTree(const std::string& rpcPath, libyang::S_Data_Node output)
+using SiblingColl = libyang::Collection<libyang::DataNode, libyang::IterationType::Sibling>;
+using DfsColl = libyang::Collection<libyang::DataNode, libyang::IterationType::Dfs>;
+
+template
+void lyNodesToTree<SiblingColl>(DatastoreAccess::Tree& res, SiblingColl items, std::optional<std::string> ignoredXPathPrefix);
+template
+void lyNodesToTree<DfsColl>(DatastoreAccess::Tree& res, DfsColl items, std::optional<std::string> ignoredXPathPrefix);
+template
+void lyNodesToTree<libyang::Set<libyang::DataNode>>(DatastoreAccess::Tree& res, libyang::Set<libyang::DataNode> items, std::optional<std::string> ignoredXPathPrefix);
+
+DatastoreAccess::Tree rpcOutputToTree(libyang::DataNode output)
 {
     DatastoreAccess::Tree res;
-    if (output) {
-        // The output is "some top-level node". If we actually want the output of our RPC/action we need to use
-        // find_path.  Also, our `path` is fully prefixed, but the output paths aren't. So we use outputNode->path() to
-        // get the unprefixed path.
-
-        auto outputNode = output->find_path(rpcPath.c_str())->data().front();
-        lyNodesToTree(res, {outputNode}, joinPaths(outputNode->path(), "/"));
-    }
+    lyNodesToTree(res, output.siblings(), joinPaths(std::string{output.path()}, "/"));
     return res;
 }
 
-libyang::S_Data_Node treeToRpcInput(libyang::S_Context ctx, const std::string& path, DatastoreAccess::Tree in)
+libyang::DataNode treeToRpcInput(libyang::Context ctx, const std::string& path, DatastoreAccess::Tree in)
 {
-    auto root = std::make_shared<libyang::Data_Node>(ctx, path.c_str(), nullptr, LYD_ANYDATA_CONSTSTRING, LYD_PATH_OPT_UPDATE);
+    auto root = ctx.newPath(path.c_str(), nullptr, libyang::CreationOptions::Update);
     for (const auto& [k, v] : in) {
-        root->new_path(ctx, k.c_str(), leafDataToString(v).c_str(), LYD_ANYDATA_CONSTSTRING, LYD_PATH_OPT_UPDATE);
+        root.newPath(k.c_str(), leafDataToString(v).c_str(), libyang::CreationOptions::Update);
     }
 
     return root;