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/yang_access.cpp b/src/yang_access.cpp
index 0555616..72e38e7 100644
--- a/src/yang_access.cpp
+++ b/src/yang_access.cpp
@@ -2,8 +2,8 @@
 #include <experimental/iterator>
 #include <fstream>
 #include <iostream>
-#include <libyang/Tree_Data.hpp>
-#include <libyang/libyang.h>
+#include <libyang-cpp/DataNode.hpp>
+#include <libyang-cpp/Utils.hpp>
 #include "UniqueResource.hpp"
 #include "libyang_utils.hpp"
 #include "utils.hpp"
@@ -11,35 +11,21 @@
 #include "yang_schema.hpp"
 
 namespace {
-template <typename Type> using lyPtrDeleter_type = void (*)(Type*);
-template <typename Type> const lyPtrDeleter_type<Type> lyPtrDeleter;
-template <> const auto lyPtrDeleter<ly_set> = ly_set_free;
-template <> const auto lyPtrDeleter<ly_ctx> = static_cast<lyPtrDeleter_type<ly_ctx>>([] (auto* ptr) {ly_ctx_destroy(ptr, nullptr);});
-template <> const auto lyPtrDeleter<lyd_node> = lyd_free_withsiblings;
-
-template <typename Type>
-auto lyWrap(Type* ptr)
-{
-    return std::unique_ptr<Type, lyPtrDeleter_type<Type>>{ptr, lyPtrDeleter<Type>};
-}
-
 // Convenient for functions that take m_datastore as an argument
-using DatastoreType = std::unique_ptr<lyd_node, lyPtrDeleter_type<lyd_node>>;
+using DatastoreType = std::optional<libyang::DataNode>;
 }
 
 YangAccess::YangAccess()
-    : m_ctx(lyWrap(ly_ctx_new(nullptr, LY_CTX_DISABLE_SEARCHDIR_CWD)))
-    , m_datastore(lyWrap<lyd_node>(nullptr))
-    , m_schema(std::make_shared<YangSchema>(libyang::create_new_Context(m_ctx.get())))
-    , m_validation_mode(LYD_OPT_DATA)
+    : m_ctx(nullptr, libyang::ContextOptions::DisableSearchCwd)
+    , m_datastore(std::nullopt)
+    , m_schema(std::make_shared<YangSchema>(m_ctx))
 {
 }
 
 YangAccess::YangAccess(std::shared_ptr<YangSchema> schema)
-    : m_ctx(schema->m_context->swig_ctx(), [](auto) {})
-    , m_datastore(lyWrap<lyd_node>(nullptr))
+    : m_ctx(schema->m_context)
+    , m_datastore(std::nullopt)
     , m_schema(schema)
-    , m_validation_mode(LYD_OPT_RPC)
 {
 }
 
@@ -47,78 +33,69 @@
 
 [[noreturn]] void YangAccess::getErrorsAndThrow() const
 {
-    auto errors = libyang::get_ly_errors(libyang::create_new_Context(m_ctx.get()));
     std::vector<DatastoreError> errorsRes;
-    for (const auto& error : errors) {
-        using namespace std::string_view_literals;
-        errorsRes.emplace_back(error->errmsg(), error->errpath() != ""sv ? std::optional{error->errpath()} : std::nullopt);
-    }
 
+    for (const auto& err : m_ctx.getErrors()) {
+        errorsRes.emplace_back(err.message, err.path);
+    }
     throw DatastoreException(errorsRes);
 }
 
 void YangAccess::impl_newPath(const std::string& path, const std::optional<std::string>& value)
 {
-    auto newNode = lyd_new_path(m_datastore.get(), m_ctx.get(), path.c_str(), value ? (void*)value->c_str() : nullptr, LYD_ANYDATA_CONSTSTRING, LYD_PATH_OPT_UPDATE);
-    if (!newNode) {
+    try {
+        if (m_datastore) {
+            m_datastore->newPath(path.c_str(), value ? value->c_str() : nullptr, libyang::CreationOptions::Update);
+        } else {
+            m_datastore = m_ctx.newPath(path.c_str(), value ? value->c_str() : nullptr, libyang::CreationOptions::Update);
+        }
+    } catch (libyang::Error&) {
         getErrorsAndThrow();
     }
-    if (!m_datastore) {
-        m_datastore = lyWrap(newNode);
-    }
 }
 
 namespace {
-void impl_unlink(DatastoreType& datastore, lyd_node* what)
+void impl_unlink(DatastoreType& datastore, libyang::DataNode what)
 {
     // If the node to be unlinked is the one our datastore variable points to, we need to find a new one to point to (one of its siblings)
-    if (datastore.get() == what) {
-        auto oldDatastore = datastore.release();
-        if (oldDatastore->prev != oldDatastore) {
-            datastore = lyWrap(oldDatastore->prev);
-        } else {
-            datastore = lyWrap(oldDatastore->next);
-        }
+
+    if (datastore == what) {
+        auto oldDatastore = datastore;
+        do {
+            datastore = datastore->previousSibling();
+            if (datastore == oldDatastore) {
+                // We have gone all the way back to our original node, which means it's the only node in our
+                // datastore.
+                datastore = std::nullopt;
+                break;
+            }
+        } while (datastore->schema().module().name() == "ietf-yang-library");
     }
 
-    lyd_unlink(what);
+    what.unlink();
 }
 }
 
 void YangAccess::impl_removeNode(const std::string& path)
 {
-    auto set = lyWrap(lyd_find_path(m_datastore.get(), path.c_str()));
-    if (!set || set->number == 0) {
-        // Check if schema node exists - lyd_find_path first checks if the first argument is non-null before checking for path validity
-        if (!ly_ctx_get_node(m_ctx.get(), nullptr, path.c_str(), 0)) {
-            throw DatastoreException{{DatastoreError{"Schema node doesn't exist.", path}}};
-        }
-        // Check if libyang found another error
-        if (ly_err_first(m_ctx.get())) {
-            getErrorsAndThrow();
-        }
-
+    if (!m_datastore) {
+        // Otherwise the datastore just doesn't contain the wanted node.
+        throw DatastoreException{{DatastoreError{"Datastore is empty.", path}}};
+    }
+    auto toRemove = m_datastore->findPath(path.c_str());
+    if (!toRemove) {
         // Otherwise the datastore just doesn't contain the wanted node.
         throw DatastoreException{{DatastoreError{"Data node doesn't exist.", path}}};
     }
 
-    auto toRemove = set->set.d[0];
-
-    impl_unlink(m_datastore, toRemove);
-
-    lyd_free(toRemove);
+    impl_unlink(m_datastore, *toRemove);
 }
 
 void YangAccess::validate()
 {
-    auto datastore = m_datastore.release();
-
-    if (m_validation_mode == LYD_OPT_RPC) {
-        lyd_validate(&datastore, m_validation_mode, nullptr);
-    } else {
-        lyd_validate(&datastore, m_validation_mode | LYD_OPT_DATA_NO_YANGLIB, m_ctx.get());
+    if (m_datastore) {
+        libyang::validateAll(m_datastore);
     }
-    m_datastore = lyWrap(datastore);
 }
 
 DatastoreAccess::Tree YangAccess::getItems(const std::string& path) const
@@ -128,10 +105,9 @@
         return res;
     }
 
-    auto set = lyWrap(lyd_find_path(m_datastore.get(), path == "/" ? "/*" : path.c_str()));
-    auto setWrapper = libyang::Set(set.get(), nullptr);
-    std::optional<std::string> ignoredXPathPrefix;
-    lyNodesToTree(res, setWrapper.data());
+    auto set = m_datastore->findXPath(path == "/" ? "/*" : path.c_str());
+
+    lyNodesToTree(res, set);
     return res;
 }
 
@@ -154,66 +130,60 @@
 namespace {
 struct impl_moveItem {
     DatastoreType& m_datastore;
-    lyd_node* m_sourceNode;
+    libyang::DataNode m_sourceNode;
 
     void operator()(yang::move::Absolute absolute) const
     {
-        auto set = lyWrap(lyd_find_instance(m_sourceNode, m_sourceNode->schema));
-        if (set->number == 1) { // m_sourceNode is the sole instance, do nothing
+        auto set = m_sourceNode.findXPath(m_sourceNode.schema().path().get().get());
+        if (set.size() == 1) { // m_sourceNode is the sole instance, do nothing
             return;
         }
 
-        doUnlink();
         switch (absolute) {
         case yang::move::Absolute::Begin:
-            if (set->set.d[0] == m_sourceNode) { // List is already at the beginning, do nothing
+            if (set.front() == m_sourceNode) { // List is already at the beginning, do nothing
                 return;
             }
-            lyd_insert_before(set->set.d[0], m_sourceNode);
-            return;
+            set.front().insertBefore(m_sourceNode);
+            break;
         case yang::move::Absolute::End:
-            if (set->set.d[set->number - 1] == m_sourceNode) { // List is already at the end, do nothing
+            if (set.back() == m_sourceNode) { // List is already at the end, do nothing
                 return;
             }
-            lyd_insert_after(set->set.d[set->number - 1], m_sourceNode);
-            return;
+            set.back().insertAfter(m_sourceNode);
+            break;
         }
+        m_datastore = m_datastore->firstSibling();
     }
 
     void operator()(const yang::move::Relative& relative) const
     {
-        auto keySuffix = m_sourceNode->schema->nodetype == LYS_LIST ? instanceToString(relative.m_path)
+        auto keySuffix = m_sourceNode.schema().nodeType() == libyang::NodeType::List ? instanceToString(relative.m_path)
                                                                     : leafDataToString(relative.m_path.at("."));
-        lyd_node* destNode;
-        lyd_find_sibling_val(m_sourceNode, m_sourceNode->schema, keySuffix.c_str(), &destNode);
+        auto destNode = m_sourceNode.findSiblingVal(m_sourceNode.schema(), keySuffix.c_str());
 
-        doUnlink();
         if (relative.m_position == yang::move::Relative::Position::After) {
-            lyd_insert_after(destNode, m_sourceNode);
+            destNode->insertAfter(m_sourceNode);
         } else {
-            lyd_insert_before(destNode, m_sourceNode);
+            destNode->insertBefore(m_sourceNode);
         }
     }
-
-private:
-    void doUnlink() const
-    {
-        impl_unlink(m_datastore, m_sourceNode);
-    }
 };
 }
 
 void YangAccess::moveItem(const std::string& source, std::variant<yang::move::Absolute, yang::move::Relative> move)
 {
-    auto set = lyWrap(lyd_find_path(m_datastore.get(), source.c_str()));
-    if (!set) { // Error, the node probably doesn't exist in the schema
-        getErrorsAndThrow();
+    if (!m_datastore) {
+        throw DatastoreException{{DatastoreError{"Datastore is empty.", source}}};
     }
-    if (set->number == 0) {
-        return;
+
+    auto sourceNode = m_datastore->findPath(source.c_str());
+
+    if (!sourceNode) {
+        // The datastore doesn't contain the wanted node.
+        throw DatastoreException{{DatastoreError{"Data node doesn't exist.", source}}};
     }
-    auto sourceNode = set->set.d[0];
-    std::visit(impl_moveItem{m_datastore, sourceNode}, move);
+    std::visit(impl_moveItem{m_datastore, *sourceNode}, move);
 }
 
 void YangAccess::commitChanges()
@@ -227,16 +197,24 @@
 
 [[noreturn]] DatastoreAccess::Tree YangAccess::execute(const std::string& path, const Tree& input)
 {
-    auto root = lyWrap(lyd_new_path(nullptr, m_ctx.get(), path.c_str(), nullptr, LYD_ANYDATA_CONSTSTRING, 0));
-    if (!root) {
-        getErrorsAndThrow();
-    }
+    auto root = [&path, this]  {
+        try {
+            return m_ctx.newPath(path.c_str());
+        } catch (libyang::ErrorWithCode& err) {
+            getErrorsAndThrow();
+        }
+    }();
+
     for (const auto& [k, v] : input) {
         if (v.type() == typeid(special_) && boost::get<special_>(v).m_value != SpecialValue::PresenceContainer) {
             continue;
         }
 
-        lyd_new_path(root.get(), m_ctx.get(), k.c_str(), (void*)leafDataToString(v).c_str(), LYD_ANYDATA_CONSTSTRING, LYD_PATH_OPT_UPDATE);
+        try {
+            root.newPath(k.c_str(), leafDataToString(v).c_str(), libyang::CreationOptions::Update);
+        } catch (libyang::ErrorWithCode& err) {
+            getErrorsAndThrow();
+        }
     }
     throw std::logic_error("in-memory datastore doesn't support executing RPC/action");
 }
@@ -244,7 +222,7 @@
 void YangAccess::copyConfig(const Datastore source, const Datastore dest)
 {
     if (source == Datastore::Startup && dest == Datastore::Running) {
-        m_datastore = nullptr;
+        m_datastore = std::nullopt;
     }
 }
 
@@ -260,16 +238,14 @@
         return res;
     }
 
-    auto instances = lyWrap(lyd_find_path(m_datastore.get(), path.c_str()));
-    auto instancesWrapper = libyang::Set(instances.get(), nullptr);
-    for (const auto& list : instancesWrapper.data()) {
+    auto instances = m_datastore->findXPath(path.c_str());
+    for (const auto& list : instances) {
         ListInstance instance;
-        for (const auto& child : list->child()->tree_for()) {
-            if (child->schema()->nodetype() == LYS_LEAF) {
-                libyang::Schema_Node_Leaf leafSchema(child->schema());
-                if (leafSchema.is_key()) {
-                    auto leafData = std::make_shared<libyang::Data_Node_Leaf_List>(child);
-                    instance.insert({leafSchema.name(), leafValueFromNode(leafData)});
+        for (const auto& child : list.child()->siblings()) {
+            if (child.schema().nodeType() == libyang::NodeType::Leaf) {
+                auto leafSchema(child.schema().asLeaf());
+                if (leafSchema.isKey()) {
+                    instance.insert({std::string{leafSchema.name()}, leafValueFromNode(child.asTerm())});
                 }
             }
         }
@@ -280,16 +256,16 @@
 
 std::string YangAccess::dump(const DataFormat format) const
 {
-    char* output;
-    lyd_print_mem(&output, m_datastore.get(), format == DataFormat::Xml ? LYD_XML : LYD_JSON, LYP_WITHSIBLINGS | LYP_FORMAT);
-    std::unique_ptr<char, decltype(&free)> deleter{output, free};
-
-    if (output) {
-        std::string res = output;
-        return res;
+    if (!m_datastore) {
+        return "";
     }
 
-    return "";
+    auto str = m_datastore->firstSibling().printStr(format == DataFormat::Xml ? libyang::DataFormat::XML : libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings);
+    if (!str) {
+        return "";
+    }
+
+    return std::string{*str};
 }
 
 void YangAccess::loadModule(const std::string& name)
@@ -307,9 +283,9 @@
     m_schema->addSchemaDirectory(path.c_str());
 }
 
-void YangAccess::enableFeature(const std::string& module, const std::string& feature)
+void YangAccess::setEnabledFeatures(const std::string& module, const std::vector<std::string>& features)
 {
-    m_schema->enableFeature(module, feature);
+    m_schema->setEnabledFeatures(module, features);
 }
 
 void YangAccess::addDataFile(const std::string& path, const StrictDataParsing strict)
@@ -320,20 +296,16 @@
 
     std::cout << "Parsing \"" << path << "\" as " << (firstChar == '{' ? "JSON" : "XML") << "...\n";
 
-    auto parseFlags = LYD_OPT_DATA | LYD_OPT_DATA_NO_YANGLIB | LYD_OPT_TRUSTED;
-    if (strict == StrictDataParsing::Yes) {
-        parseFlags |= LYD_OPT_STRICT;
-    }
-    auto dataNode = lyd_parse_path(m_ctx.get(), path.c_str(), firstChar == '{' ? LYD_JSON : LYD_XML, parseFlags);
-
-    if (!dataNode) {
-        throw std::runtime_error("Supplied data file " + path + " couldn't be parsed.");
-    }
+    auto dataNode = m_ctx.parseDataPath(
+            path.c_str(),
+            firstChar == '{' ? libyang::DataFormat::JSON : libyang::DataFormat::XML,
+            strict == StrictDataParsing::Yes ? std::optional{libyang::ParseOptions::Strict} : std::nullopt,
+            libyang::ValidationOptions::Present);
 
     if (!m_datastore) {
-        m_datastore = lyWrap(dataNode);
+        m_datastore = dataNode;
     } else {
-        lyd_merge(m_datastore.get(), dataNode, LYD_OPT_DESTRUCT);
+        m_datastore->merge(*dataNode);
     }
 
     validate();