mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-22 01:13:00 +02:00
Bump sysrepo,lignetconf2, libyang and netopeer2
This update got pretty messy, since libyang had some breaking changes. And unfortunatly rousette, libyang-cpp and sysrepo-cpp has not made any new release yet.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
From 22b41e7ed8268b94ccc139138e2037c390a3a616 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Bed=C5=99ich=20Schindler?= <bedrich.schindler@gmail.com>
|
||||
Date: Thu, 10 Jul 2025 15:00:45 +0200
|
||||
Subject: [PATCH] Implement `SchemaNode::actionRpcs()`
|
||||
Subject: [PATCH 1/7] Implement `SchemaNode::actionRpcs()`
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
From 1f8e502cf0308faa0336b1077308184de6a83ee0 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= <jan.kundrat@cesnet.cz>
|
||||
Date: Mon, 20 Oct 2025 11:45:06 +0200
|
||||
Subject: [PATCH 2/7] CI: pin to libyang v3
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Organization: Wires
|
||||
|
||||
Change-Id: Idba085ad9c9c137874801f1d4089f8ee6bb390c7
|
||||
Signed-off-by: Mattias Walström <lazzer@gmail.com>
|
||||
---
|
||||
.zuul.yaml | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/.zuul.yaml b/.zuul.yaml
|
||||
index 7b92766..fd89549 100644
|
||||
--- a/.zuul.yaml
|
||||
+++ b/.zuul.yaml
|
||||
@@ -4,13 +4,13 @@
|
||||
- f38-gcc-cover:
|
||||
required-projects:
|
||||
- name: github/CESNET/libyang
|
||||
- override-checkout: devel
|
||||
+ override-checkout: cesnet/2025-08-07
|
||||
- name: github/doctest/doctest
|
||||
override-checkout: v2.3.6
|
||||
- f38-clang-asan-ubsan:
|
||||
required-projects: &projects
|
||||
- name: github/CESNET/libyang
|
||||
- override-checkout: devel
|
||||
+ override-checkout: cesnet/2025-08-07
|
||||
- name: github/doctest/doctest
|
||||
override-checkout: v2.4.11
|
||||
- f38-clang-tsan:
|
||||
--
|
||||
2.43.0
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
From b27a65d5d5f6ffffbd2c26c1b5bea57a6de584d0 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Pecka?= <tomas.pecka@cesnet.cz>
|
||||
Date: Mon, 20 Oct 2025 18:46:38 +0200
|
||||
Subject: [PATCH 3/7] wrap lyd_validate_op
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Organization: Wires
|
||||
|
||||
This patch wraps lyd_validate_op from libyang. Downstream users can now
|
||||
validate operations (RPC input, replies, and notifications).
|
||||
|
||||
Change-Id: Ib03070bbc3e1d0dccd6bf38eda82e944db1093b1
|
||||
Signed-off-by: Mattias Walström <lazzer@gmail.com>
|
||||
---
|
||||
include/libyang-cpp/DataNode.hpp | 2 +
|
||||
src/DataNode.cpp | 24 +++++++++++
|
||||
tests/context.cpp | 2 +-
|
||||
tests/data_node.cpp | 72 ++++++++++++++++++++++++++++++++
|
||||
tests/example_schema.hpp | 22 ++++++++++
|
||||
5 files changed, 121 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/include/libyang-cpp/DataNode.hpp b/include/libyang-cpp/DataNode.hpp
|
||||
index 50d6c0e..e202a96 100644
|
||||
--- a/include/libyang-cpp/DataNode.hpp
|
||||
+++ b/include/libyang-cpp/DataNode.hpp
|
||||
@@ -59,6 +59,7 @@ template <typename Operation, typename Siblings>
|
||||
void handleLyTreeOperation(DataNode* affectedNode, Operation operation, Siblings siblings, std::shared_ptr<internal_refcount> newRefs);
|
||||
|
||||
LIBYANG_CPP_EXPORT void validateAll(std::optional<libyang::DataNode>& node, const std::optional<ValidationOptions>& opts = std::nullopt);
|
||||
+LIBYANG_CPP_EXPORT void validateOp(libyang::DataNode& input, const std::optional<libyang::DataNode>& opsTree, OperationType opType);
|
||||
|
||||
LIBYANG_CPP_EXPORT Set<DataNode> findXPathAt(
|
||||
const std::optional<libyang::DataNode>& contextNode,
|
||||
@@ -147,6 +148,7 @@ public:
|
||||
friend LIBYANG_CPP_EXPORT lyd_node* getRawNode(DataNode node);
|
||||
|
||||
friend LIBYANG_CPP_EXPORT void validateAll(std::optional<libyang::DataNode>& node, const std::optional<ValidationOptions>& opts);
|
||||
+ friend LIBYANG_CPP_EXPORT void validateOp(libyang::DataNode& input, const std::optional<libyang::DataNode>& opsTree, OperationType opType);
|
||||
friend LIBYANG_CPP_EXPORT Set<DataNode> findXPathAt(const std::optional<libyang::DataNode>& contextNode, const libyang::DataNode& forest, const std::string& xpath);
|
||||
|
||||
bool operator==(const DataNode& node) const;
|
||||
diff --git a/src/DataNode.cpp b/src/DataNode.cpp
|
||||
index 7e87917..e28ef30 100644
|
||||
--- a/src/DataNode.cpp
|
||||
+++ b/src/DataNode.cpp
|
||||
@@ -1226,6 +1226,30 @@ void validateAll(std::optional<libyang::DataNode>& node, const std::optional<Val
|
||||
}
|
||||
}
|
||||
|
||||
+/** @brief Validate op after parsing with lyd_parse_op.
|
||||
+ *
|
||||
+ * Wraps `lyd_validate_op`.
|
||||
+ *
|
||||
+ * @param input The tree with the op to validate.
|
||||
+ * @param opsTree The optional data tree to validate the input against.
|
||||
+ * @param opType The operation type. Contrary to `lyd_validate_op`, we accept not only YANG but also NETCONF and RESTCONF operation types (and internally convert them to YANG).
|
||||
+ */
|
||||
+void validateOp(libyang::DataNode& input, const std::optional<libyang::DataNode>& opsTree, OperationType opType)
|
||||
+{
|
||||
+ if (opType == OperationType::RpcYang || opType == OperationType::RpcRestconf || opType == OperationType::RpcNetconf) {
|
||||
+ opType = OperationType::RpcYang;
|
||||
+ } else if (opType == OperationType::ReplyYang || opType == OperationType::ReplyRestconf || opType == OperationType::ReplyNetconf) {
|
||||
+ opType = OperationType::ReplyYang;
|
||||
+ } else if (opType == OperationType::NotificationYang || opType == OperationType::NotificationRestconf || opType == OperationType::NotificationNetconf) {
|
||||
+ opType = OperationType::NotificationYang;
|
||||
+ } else {
|
||||
+ throw Error("validateOp: DataYang datatype is not supported");
|
||||
+ }
|
||||
+
|
||||
+ auto ret = lyd_validate_op(input.m_node, opsTree ? opsTree->m_node : nullptr, utils::toOpType(opType), nullptr);
|
||||
+ throwIfError(ret, "libyang:validateOp: lyd_validate_op failed");
|
||||
+}
|
||||
+
|
||||
/** @short Find instances matching the provided XPath
|
||||
*
|
||||
* @param contextNode The node which serves as the "context node" for XPath evaluation. Use nullopt to start at root.
|
||||
diff --git a/tests/context.cpp b/tests/context.cpp
|
||||
index 6c5dde8..c0b7e09 100644
|
||||
--- a/tests/context.cpp
|
||||
+++ b/tests/context.cpp
|
||||
@@ -154,7 +154,7 @@ TEST_CASE("context")
|
||||
{
|
||||
auto mod = ctx->parseModule(example_schema, libyang::SchemaFormat::YANG);
|
||||
auto rpcs = mod.actionRpcs();
|
||||
- REQUIRE(rpcs.size() == 1);
|
||||
+ REQUIRE(rpcs.size() == 2);
|
||||
REQUIRE(rpcs[0].module().name() == "example-schema");
|
||||
REQUIRE(rpcs[0].name() == "myRpc");
|
||||
|
||||
diff --git a/tests/data_node.cpp b/tests/data_node.cpp
|
||||
index db5a28e..9215b12 100644
|
||||
--- a/tests/data_node.cpp
|
||||
+++ b/tests/data_node.cpp
|
||||
@@ -2430,6 +2430,78 @@ TEST_CASE("Data Node manipulation")
|
||||
"Can't parse into operation data tree: LY_EVALID", libyang::Error);
|
||||
}
|
||||
}
|
||||
+
|
||||
+ DOCTEST_SUBCASE("Validation")
|
||||
+ {
|
||||
+ DOCTEST_SUBCASE("Valid input")
|
||||
+ {
|
||||
+ std::string rpcInput;
|
||||
+ std::string rpcPath;
|
||||
+ std::string expected;
|
||||
+ std::optional<libyang::DataNode> depTree;
|
||||
+
|
||||
+ DOCTEST_SUBCASE("RPC")
|
||||
+ {
|
||||
+ rpcInput = R"({"example-schema:input": { "number": 42 } })";
|
||||
+ rpcPath = "/example-schema:rpc-with-choice";
|
||||
+ expected = R"({
|
||||
+ "example-schema:rpc-with-choice": {
|
||||
+ "number": 42
|
||||
+ }
|
||||
+}
|
||||
+)";
|
||||
+ }
|
||||
+
|
||||
+ DOCTEST_SUBCASE("Action")
|
||||
+ {
|
||||
+ rpcInput = R"({ "example-schema:input": { "friend": "Kuba" } })";
|
||||
+ rpcPath = "/example-schema:person[name='Franta']/poke-a-friend";
|
||||
+ expected = R"({
|
||||
+ "example-schema:poke-a-friend": {
|
||||
+ "friend": "Kuba"
|
||||
+ }
|
||||
+}
|
||||
+)";
|
||||
+ depTree = ctx.newPath("/example-schema:person[name='Kuba']");
|
||||
+ }
|
||||
+
|
||||
+ auto [parent, rpcTree] = ctx.newPath2(rpcPath);
|
||||
+ auto rpcOp = rpcTree->parseOp(rpcInput, dataTypeFor(rpcInput), libyang::OperationType::RpcRestconf);
|
||||
+
|
||||
+ REQUIRE(!rpcOp.op);
|
||||
+ REQUIRE(rpcOp.tree);
|
||||
+
|
||||
+ libyang::validateOp(*rpcTree, depTree, libyang::OperationType::RpcRestconf);
|
||||
+ REQUIRE(*rpcTree->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::KeepEmptyCont) == expected);
|
||||
+ }
|
||||
+
|
||||
+ DOCTEST_SUBCASE("Nodes in disjunctive cases defined together")
|
||||
+ {
|
||||
+ auto rpcInput = R"({ "example-schema:input": { "number": 42, "text": "The ultimate answer" } })";
|
||||
+
|
||||
+ auto rpcTree = ctx.newPath("/example-schema:rpc-with-choice");
|
||||
+ auto rpcOp = rpcTree.parseOp(rpcInput, dataTypeFor(rpcInput), libyang::OperationType::RpcRestconf);
|
||||
+
|
||||
+ REQUIRE(!rpcOp.op);
|
||||
+ REQUIRE(rpcOp.tree);
|
||||
+
|
||||
+ REQUIRE_THROWS_WITH_AS(libyang::validateOp(rpcTree, std::nullopt, libyang::OperationType::RpcRestconf), "libyang:validateOp: lyd_validate_op failed: LY_EVALID", libyang::Error);
|
||||
+ }
|
||||
+
|
||||
+ DOCTEST_SUBCASE("Action without the leafref node")
|
||||
+ {
|
||||
+ auto rpcInput = R"({ "example-schema:input": { "friend": "Kuba" } })";
|
||||
+ auto rpcPath = "/example-schema:person[name='Franta']/poke-a-friend";
|
||||
+
|
||||
+ auto [parent, rpcTree] = ctx.newPath2(rpcPath);
|
||||
+ auto rpcOp = rpcTree->parseOp(rpcInput, dataTypeFor(rpcInput), libyang::OperationType::RpcRestconf);
|
||||
+
|
||||
+ REQUIRE(!rpcOp.op);
|
||||
+ REQUIRE(rpcOp.tree);
|
||||
+
|
||||
+ REQUIRE_THROWS_WITH_AS(libyang::validateOp(*rpcTree, std::nullopt, libyang::OperationType::RpcRestconf), "libyang:validateOp: lyd_validate_op failed: LY_EVALID", libyang::Error);
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
DOCTEST_SUBCASE("comparing") {
|
||||
diff --git a/tests/example_schema.hpp b/tests/example_schema.hpp
|
||||
index 02d3d74..acc6ecc 100644
|
||||
--- a/tests/example_schema.hpp
|
||||
+++ b/tests/example_schema.hpp
|
||||
@@ -108,6 +108,16 @@ module example-schema {
|
||||
}
|
||||
|
||||
action poke { }
|
||||
+
|
||||
+ action poke-a-friend {
|
||||
+ input {
|
||||
+ leaf friend {
|
||||
+ type leafref {
|
||||
+ path '../../../person/name';
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
leaf bossPerson {
|
||||
@@ -284,6 +294,18 @@ module example-schema {
|
||||
}
|
||||
}
|
||||
}
|
||||
+ rpc rpc-with-choice {
|
||||
+ input {
|
||||
+ choice the-impossible-choice {
|
||||
+ leaf text {
|
||||
+ type string;
|
||||
+ }
|
||||
+ leaf number {
|
||||
+ type int32;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
|
||||
anydata myData {
|
||||
}
|
||||
--
|
||||
2.43.0
|
||||
|
||||
@@ -0,0 +1,435 @@
|
||||
From 4c00c24dee6bbc694d62e7174b7ac14f8aa95f14 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= <jan.kundrat@cesnet.cz>
|
||||
Date: Thu, 30 Oct 2025 21:16:13 +0100
|
||||
Subject: [PATCH 4/7] port to libyang v4
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Organization: Wires
|
||||
|
||||
Apart from the usual API/ABI changes, the default values of a `leaf` or
|
||||
a `leaf-list` are no longer canonicalized.
|
||||
|
||||
This used to be missing from libyang v4, but it was added recently.
|
||||
Sill, chances are that the form which was used in the YANG model's
|
||||
source code is actually *the* intended presentation, so let's skip
|
||||
canonicalization altogether.
|
||||
|
||||
Upstream has removed some flags and defines, so let's sync their list
|
||||
with whatever is currently available in the `devel` branch.
|
||||
|
||||
Change-Id: I52dcec88eeee003b45dd6cf400f4d6875abbcc05
|
||||
Signed-off-by: Mattias Walström <lazzer@gmail.com>
|
||||
---
|
||||
.zuul.yaml | 4 ++--
|
||||
CMakeLists.txt | 4 ++--
|
||||
README.md | 2 +-
|
||||
include/libyang-cpp/Context.hpp | 6 +++++-
|
||||
include/libyang-cpp/DataNode.hpp | 5 ++++-
|
||||
include/libyang-cpp/Enum.hpp | 15 +++++++++++++--
|
||||
src/Context.cpp | 12 ++++++++++--
|
||||
src/DataNode.cpp | 11 +++++++++--
|
||||
src/SchemaNode.cpp | 6 +++---
|
||||
src/Type.cpp | 2 +-
|
||||
src/utils/enum.hpp | 21 +++++++++++++++++++--
|
||||
tests/context.cpp | 32 +++++++++++++++++---------------
|
||||
tests/data_node.cpp | 2 +-
|
||||
tests/schema_node.cpp | 4 ++--
|
||||
14 files changed, 89 insertions(+), 37 deletions(-)
|
||||
|
||||
diff --git a/.zuul.yaml b/.zuul.yaml
|
||||
index fd89549..7b92766 100644
|
||||
--- a/.zuul.yaml
|
||||
+++ b/.zuul.yaml
|
||||
@@ -4,13 +4,13 @@
|
||||
- f38-gcc-cover:
|
||||
required-projects:
|
||||
- name: github/CESNET/libyang
|
||||
- override-checkout: cesnet/2025-08-07
|
||||
+ override-checkout: devel
|
||||
- name: github/doctest/doctest
|
||||
override-checkout: v2.3.6
|
||||
- f38-clang-asan-ubsan:
|
||||
required-projects: &projects
|
||||
- name: github/CESNET/libyang
|
||||
- override-checkout: cesnet/2025-08-07
|
||||
+ override-checkout: devel
|
||||
- name: github/doctest/doctest
|
||||
override-checkout: v2.4.11
|
||||
- f38-clang-tsan:
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index c6db320..90f9aaa 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -20,7 +20,7 @@ add_custom_target(libyang-cpp-version-cmake
|
||||
cmake/ProjectGitVersionRunner.cmake
|
||||
)
|
||||
include(cmake/ProjectGitVersion.cmake)
|
||||
-set(LIBYANG_CPP_PKG_VERSION "4")
|
||||
+set(LIBYANG_CPP_PKG_VERSION "5")
|
||||
prepare_git_version(LIBYANG_CPP_VERSION ${LIBYANG_CPP_PKG_VERSION})
|
||||
|
||||
find_package(Doxygen)
|
||||
@@ -28,7 +28,7 @@ option(WITH_DOCS "Create and install internal documentation (needs Doxygen)" ${D
|
||||
option(BUILD_SHARED_LIBS "By default, shared libs are enabled. Turn off for a static build." ON)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
-pkg_check_modules(LIBYANG REQUIRED libyang>=3.10.1 IMPORTED_TARGET)
|
||||
+pkg_check_modules(LIBYANG REQUIRED libyang>=4.1.0 IMPORTED_TARGET)
|
||||
|
||||
# FIXME from gcc 14.1 on we should be able to use the calendar/time from libstdc++ and thus remove the date dependency
|
||||
find_package(date)
|
||||
diff --git a/README.md b/README.md
|
||||
index 8291e00..0cef2ff 100644
|
||||
--- a/README.md
|
||||
+++ b/README.md
|
||||
@@ -8,7 +8,7 @@
|
||||
Object lifetimes are managed automatically via RAII.
|
||||
|
||||
## Dependencies
|
||||
-- [libyang v3](https://github.com/CESNET/libyang) - the `devel` branch (even for the `master` branch of *libyang-cpp*)
|
||||
+- [libyang v4](https://github.com/CESNET/libyang) - the `devel` branch (even for the `master` branch of *libyang-cpp*)
|
||||
- C++20 compiler (e.g., GCC 10.x+, clang 10+)
|
||||
- CMake 3.19+
|
||||
- optionally for built-in tests, [Doctest](https://github.com/doctest/doctest/) as a C++ unit test framework
|
||||
diff --git a/include/libyang-cpp/Context.hpp b/include/libyang-cpp/Context.hpp
|
||||
index ca89063..e695914 100644
|
||||
--- a/include/libyang-cpp/Context.hpp
|
||||
+++ b/include/libyang-cpp/Context.hpp
|
||||
@@ -108,7 +108,11 @@ public:
|
||||
std::optional<SubmoduleParsed> getSubmodule(const std::string& name, const std::optional<std::string>& revision) const;
|
||||
void registerModuleCallback(std::function<ModuleCallback> callback);
|
||||
|
||||
- ParsedOp parseOp(const std::string& input, const DataFormat format, const OperationType opType) const;
|
||||
+ ParsedOp parseOp(
|
||||
+ const std::string& input,
|
||||
+ const DataFormat format,
|
||||
+ const OperationType opType,
|
||||
+ const std::optional<ParseOptions> parseOpts = std::nullopt) const;
|
||||
|
||||
DataNode newPath(const std::string& path, const std::optional<std::string>& value = std::nullopt, const std::optional<CreationOptions> options = std::nullopt) const;
|
||||
CreatedNodes newPath2(const std::string& path, const std::optional<std::string>& value = std::nullopt, const std::optional<CreationOptions> options = std::nullopt) const;
|
||||
diff --git a/include/libyang-cpp/DataNode.hpp b/include/libyang-cpp/DataNode.hpp
|
||||
index e202a96..35e5936 100644
|
||||
--- a/include/libyang-cpp/DataNode.hpp
|
||||
+++ b/include/libyang-cpp/DataNode.hpp
|
||||
@@ -123,7 +123,10 @@ public:
|
||||
Collection<DataNode, IterationType::Sibling> siblings() const;
|
||||
Collection<DataNode, IterationType::Sibling> immediateChildren() const;
|
||||
|
||||
- ParsedOp parseOp(const std::string& input, const DataFormat format, const OperationType opType) const;
|
||||
+ ParsedOp parseOp(const std::string& input,
|
||||
+ const DataFormat format,
|
||||
+ const OperationType opType,
|
||||
+ const std::optional<ParseOptions> parseOpts = std::nullopt) const;
|
||||
|
||||
void parseSubtree(
|
||||
const std::string& data,
|
||||
diff --git a/include/libyang-cpp/Enum.hpp b/include/libyang-cpp/Enum.hpp
|
||||
index 85dcc83..32142f9 100644
|
||||
--- a/include/libyang-cpp/Enum.hpp
|
||||
+++ b/include/libyang-cpp/Enum.hpp
|
||||
@@ -136,6 +136,7 @@ enum class DuplicationOptions : uint32_t {
|
||||
WithFlags = 0x08,
|
||||
NoExt = 0x10,
|
||||
WithPriv = 0x20,
|
||||
+ NoLyds = 0x40,
|
||||
};
|
||||
|
||||
enum class NodeType : uint16_t {
|
||||
@@ -167,6 +168,13 @@ enum class ContextOptions : uint16_t {
|
||||
PreferSearchDirs = 0x20,
|
||||
SetPrivParsed = 0x40,
|
||||
ExplicitCompile = 0x80,
|
||||
+ EnableImpFeatures = 0x100,
|
||||
+ CompileObsolete = 0x200,
|
||||
+ LybHashes = 0x400,
|
||||
+ LeafrefExtended = 0x800,
|
||||
+ LeafrefLinking = 0x1000,
|
||||
+ BuiltinPluginsOnly = 0x2000,
|
||||
+ StaticPluginsOnly = 0x4000,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -203,7 +211,6 @@ enum class AnydataValueType : uint32_t {
|
||||
String,
|
||||
XML,
|
||||
JSON,
|
||||
- LYB
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -252,6 +259,7 @@ enum class ValidationOptions {
|
||||
MultiError = 0x0004,
|
||||
Operational = 0x0008,
|
||||
NoDefaults = 0x0010,
|
||||
+ NotFinal = 0x0020,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -262,11 +270,14 @@ enum class ParseOptions {
|
||||
Strict = 0x020000,
|
||||
Opaque = 0x040000,
|
||||
NoState = 0x080000,
|
||||
- LybModUpdate = 0x100000,
|
||||
+ LybSkipCtxCheck = 0x100000,
|
||||
Ordered = 0x200000,
|
||||
Subtree = 0x400000, /**< Do not use this one for parsing of data subtrees */
|
||||
WhenTrue = 0x800000,
|
||||
NoNew = 0x1000000,
|
||||
+ StoreOnly = 0x2010000,
|
||||
+ JsonNull = 0x4000000,
|
||||
+ JsonStringDataTypes = 0x8000000,
|
||||
};
|
||||
|
||||
/**
|
||||
diff --git a/src/Context.cpp b/src/Context.cpp
|
||||
index fec2f27..f5bdc71 100644
|
||||
--- a/src/Context.cpp
|
||||
+++ b/src/Context.cpp
|
||||
@@ -239,7 +239,7 @@ std::optional<DataNode> Context::parseExtData(
|
||||
* Note: to parse a NETCONF RPC reply, you MUST parse the original NETCONF RPC request (that is, you have to use
|
||||
* this method with OperationType::RpcNetconf).
|
||||
*/
|
||||
-ParsedOp Context::parseOp(const std::string& input, const DataFormat format, const OperationType opType) const
|
||||
+ParsedOp Context::parseOp(const std::string& input, const DataFormat format, const OperationType opType, const std::optional<ParseOptions> parseOpts) const
|
||||
{
|
||||
auto in = wrap_ly_in_new_memory(input);
|
||||
|
||||
@@ -251,7 +251,15 @@ ParsedOp Context::parseOp(const std::string& input, const DataFormat format, con
|
||||
case OperationType::NotificationYang: {
|
||||
lyd_node* op = nullptr;
|
||||
lyd_node* tree = nullptr;
|
||||
- auto err = lyd_parse_op(m_ctx.get(), nullptr, in.get(), utils::toLydFormat(format), utils::toOpType(opType), &tree, &op);
|
||||
+ auto err = lyd_parse_op(
|
||||
+ m_ctx.get(),
|
||||
+ nullptr,
|
||||
+ in.get(),
|
||||
+ utils::toLydFormat(format),
|
||||
+ utils::toOpType(opType),
|
||||
+ parseOpts ? utils::toParseOptions(*parseOpts) : 0,
|
||||
+ &tree,
|
||||
+ &op);
|
||||
|
||||
ParsedOp res;
|
||||
res.tree = tree ? std::optional{libyang::wrapRawNode(tree)} : std::nullopt;
|
||||
diff --git a/src/DataNode.cpp b/src/DataNode.cpp
|
||||
index e28ef30..edabf9b 100644
|
||||
--- a/src/DataNode.cpp
|
||||
+++ b/src/DataNode.cpp
|
||||
@@ -391,7 +391,7 @@ DataNodeAny DataNode::asAny() const
|
||||
*
|
||||
* Wraps `lyd_parse_op`.
|
||||
*/
|
||||
-ParsedOp DataNode::parseOp(const std::string& input, const DataFormat format, const OperationType opType) const
|
||||
+ParsedOp DataNode::parseOp(const std::string& input, const DataFormat format, const OperationType opType, const std::optional<ParseOptions> parseOpts) const
|
||||
{
|
||||
auto in = wrap_ly_in_new_memory(input);
|
||||
|
||||
@@ -401,7 +401,14 @@ ParsedOp DataNode::parseOp(const std::string& input, const DataFormat format, co
|
||||
case OperationType::ReplyRestconf: {
|
||||
lyd_node* op = nullptr;
|
||||
lyd_node* tree = nullptr;
|
||||
- auto err = lyd_parse_op(m_node->schema->module->ctx, m_node, in.get(), utils::toLydFormat(format), utils::toOpType(opType), &tree, nullptr);
|
||||
+ auto err = lyd_parse_op(m_node->schema->module->ctx,
|
||||
+ m_node,
|
||||
+ in.get(),
|
||||
+ utils::toLydFormat(format),
|
||||
+ utils::toOpType(opType),
|
||||
+ parseOpts ? utils::toParseOptions(*parseOpts) : 0,
|
||||
+ &tree,
|
||||
+ nullptr);
|
||||
ParsedOp res{
|
||||
.tree = tree ? std::optional{libyang::wrapRawNode(tree)} : std::nullopt,
|
||||
.op = op ? std::optional{libyang::wrapRawNode(op)} : std::nullopt
|
||||
diff --git a/src/SchemaNode.cpp b/src/SchemaNode.cpp
|
||||
index ce24203..2e0351a 100644
|
||||
--- a/src/SchemaNode.cpp
|
||||
+++ b/src/SchemaNode.cpp
|
||||
@@ -564,7 +564,7 @@ std::vector<std::string> LeafList::defaultValuesStr() const
|
||||
auto dflts = reinterpret_cast<const lysc_node_leaflist*>(m_node)->dflts;
|
||||
std::vector<std::string> res;
|
||||
for (const auto& it : std::span(dflts, LY_ARRAY_COUNT(dflts))) {
|
||||
- res.emplace_back(lyd_value_get_canonical(m_ctx.get(), it));
|
||||
+ res.emplace_back(it.str);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@@ -660,8 +660,8 @@ bool LeafList::isUserOrdered() const
|
||||
std::optional<std::string> Leaf::defaultValueStr() const
|
||||
{
|
||||
auto dflt = reinterpret_cast<const lysc_node_leaf*>(m_node)->dflt;
|
||||
- if (dflt) {
|
||||
- return lyd_value_get_canonical(m_ctx.get(), dflt);
|
||||
+ if (dflt.str) {
|
||||
+ return std::string{dflt.str};
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
diff --git a/src/Type.cpp b/src/Type.cpp
|
||||
index 01b2c4f..4d2db46 100644
|
||||
--- a/src/Type.cpp
|
||||
+++ b/src/Type.cpp
|
||||
@@ -240,7 +240,7 @@ std::optional<std::string> Type::description() const
|
||||
*/
|
||||
std::string Type::internalPluginId() const
|
||||
{
|
||||
- return m_type->plugin->id;
|
||||
+ return lysc_get_type_plugin(m_type->plugin_ref)->id;
|
||||
}
|
||||
|
||||
/**
|
||||
diff --git a/src/utils/enum.hpp b/src/utils/enum.hpp
|
||||
index 5e02d24..0b2e2d7 100644
|
||||
--- a/src/utils/enum.hpp
|
||||
+++ b/src/utils/enum.hpp
|
||||
@@ -91,6 +91,7 @@ static_assert(LYD_DUP_WITH_FLAGS == toDuplicationOptions(DuplicationOptions::Wit
|
||||
static_assert(LYD_DUP_WITH_PARENTS == toDuplicationOptions(DuplicationOptions::WithParents));
|
||||
static_assert(LYD_DUP_NO_EXT == toDuplicationOptions(DuplicationOptions::NoExt));
|
||||
static_assert(LYD_DUP_WITH_PRIV == toDuplicationOptions(DuplicationOptions::WithPriv));
|
||||
+static_assert(LYD_DUP_NO_LYDS == toDuplicationOptions(DuplicationOptions::NoLyds));
|
||||
|
||||
static_assert((LYD_DUP_NO_META | LYD_DUP_NO_EXT) ==
|
||||
toDuplicationOptions(DuplicationOptions::NoMeta | DuplicationOptions::NoExt));
|
||||
@@ -131,6 +132,13 @@ static_assert(toContextOptions(ContextOptions::DisableSearchCwd) == LY_CTX_DISAB
|
||||
static_assert(toContextOptions(ContextOptions::PreferSearchDirs) == LY_CTX_PREFER_SEARCHDIRS);
|
||||
static_assert(toContextOptions(ContextOptions::SetPrivParsed) == LY_CTX_SET_PRIV_PARSED);
|
||||
static_assert(toContextOptions(ContextOptions::ExplicitCompile) == LY_CTX_EXPLICIT_COMPILE);
|
||||
+static_assert(toContextOptions(ContextOptions::EnableImpFeatures) == LY_CTX_ENABLE_IMP_FEATURES);
|
||||
+static_assert(toContextOptions(ContextOptions::CompileObsolete) == LY_CTX_COMPILE_OBSOLETE);
|
||||
+static_assert(toContextOptions(ContextOptions::LybHashes) == LY_CTX_LYB_HASHES);
|
||||
+static_assert(toContextOptions(ContextOptions::LeafrefExtended) == LY_CTX_LEAFREF_EXTENDED);
|
||||
+static_assert(toContextOptions(ContextOptions::LeafrefLinking) == LY_CTX_LEAFREF_LINKING);
|
||||
+static_assert(toContextOptions(ContextOptions::BuiltinPluginsOnly) == LY_CTX_BUILTIN_PLUGINS_ONLY);
|
||||
+static_assert(toContextOptions(ContextOptions::StaticPluginsOnly) == LY_CTX_STATIC_PLUGINS_ONLY);
|
||||
|
||||
constexpr uint16_t toLogOptions(const LogOptions options)
|
||||
{
|
||||
@@ -198,6 +206,10 @@ constexpr uint32_t toValidationOptions(const ValidationOptions opts)
|
||||
|
||||
static_assert(toValidationOptions(ValidationOptions::NoState) == LYD_VALIDATE_NO_STATE);
|
||||
static_assert(toValidationOptions(ValidationOptions::Present) == LYD_VALIDATE_PRESENT);
|
||||
+static_assert(toValidationOptions(ValidationOptions::MultiError) == LYD_VALIDATE_MULTI_ERROR);
|
||||
+static_assert(toValidationOptions(ValidationOptions::Operational) == LYD_VALIDATE_OPERATIONAL);
|
||||
+static_assert(toValidationOptions(ValidationOptions::NoDefaults) == LYD_VALIDATE_NO_DEFAULTS);
|
||||
+static_assert(toValidationOptions(ValidationOptions::NotFinal) == LYD_VALIDATE_NOT_FINAL);
|
||||
|
||||
constexpr uint32_t toParseOptions(const ParseOptions opts)
|
||||
{
|
||||
@@ -208,8 +220,14 @@ static_assert(toParseOptions(ParseOptions::ParseOnly) == LYD_PARSE_ONLY);
|
||||
static_assert(toParseOptions(ParseOptions::Strict) == LYD_PARSE_STRICT);
|
||||
static_assert(toParseOptions(ParseOptions::Opaque) == LYD_PARSE_OPAQ);
|
||||
static_assert(toParseOptions(ParseOptions::NoState) == LYD_PARSE_NO_STATE);
|
||||
-static_assert(toParseOptions(ParseOptions::LybModUpdate) == LYD_PARSE_LYB_MOD_UPDATE);
|
||||
+static_assert(toParseOptions(ParseOptions::LybSkipCtxCheck) == LYD_PARSE_LYB_SKIP_CTX_CHECK);
|
||||
static_assert(toParseOptions(ParseOptions::Ordered) == LYD_PARSE_ORDERED);
|
||||
+static_assert(toParseOptions(ParseOptions::Subtree) == LYD_PARSE_SUBTREE);
|
||||
+static_assert(toParseOptions(ParseOptions::WhenTrue) == LYD_PARSE_WHEN_TRUE);
|
||||
+static_assert(toParseOptions(ParseOptions::NoNew) == LYD_PARSE_NO_NEW);
|
||||
+static_assert(toParseOptions(ParseOptions::StoreOnly) == LYD_PARSE_STORE_ONLY);
|
||||
+static_assert(toParseOptions(ParseOptions::JsonNull) == LYD_PARSE_JSON_NULL);
|
||||
+static_assert(toParseOptions(ParseOptions::JsonStringDataTypes) == LYD_PARSE_JSON_STRING_DATATYPES);
|
||||
|
||||
constexpr lyd_type toOpType(const OperationType type)
|
||||
{
|
||||
@@ -242,7 +260,6 @@ static_assert(toAnydataValueType(AnydataValueType::DataTree) == LYD_ANYDATA_DATA
|
||||
static_assert(toAnydataValueType(AnydataValueType::String) == LYD_ANYDATA_STRING);
|
||||
static_assert(toAnydataValueType(AnydataValueType::XML) == LYD_ANYDATA_XML);
|
||||
static_assert(toAnydataValueType(AnydataValueType::JSON) == LYD_ANYDATA_JSON);
|
||||
-static_assert(toAnydataValueType(AnydataValueType::LYB) == LYD_ANYDATA_LYB);
|
||||
|
||||
constexpr LYS_OUTFORMAT toLysOutFormat(const SchemaOutputFormat format)
|
||||
{
|
||||
diff --git a/tests/context.cpp b/tests/context.cpp
|
||||
index c0b7e09..eaedebf 100644
|
||||
--- a/tests/context.cpp
|
||||
+++ b/tests/context.cpp
|
||||
@@ -359,25 +359,27 @@ TEST_CASE("context")
|
||||
ctx->loadModule("mod1", std::nullopt, {});
|
||||
ctx->parseModule(valid_yang_model, libyang::SchemaFormat::YANG);
|
||||
auto modules = ctx->modules();
|
||||
- REQUIRE(modules.size() == 8);
|
||||
+ REQUIRE(modules.size() == 9);
|
||||
REQUIRE(modules.at(0).name() == "ietf-yang-metadata");
|
||||
REQUIRE(modules.at(0).ns() == "urn:ietf:params:xml:ns:yang:ietf-yang-metadata");
|
||||
REQUIRE(modules.at(1).name() == "yang");
|
||||
REQUIRE(modules.at(1).ns() == "urn:ietf:params:xml:ns:yang:1");
|
||||
- REQUIRE(modules.at(2).name() == "ietf-inet-types");
|
||||
- REQUIRE(modules.at(2).ns() == "urn:ietf:params:xml:ns:yang:ietf-inet-types");
|
||||
- REQUIRE(modules.at(3).name() == "ietf-yang-types");
|
||||
- REQUIRE(modules.at(3).ns() == "urn:ietf:params:xml:ns:yang:ietf-yang-types");
|
||||
- REQUIRE(modules.at(4).name() == "ietf-yang-schema-mount");
|
||||
- REQUIRE(modules.at(4).ns() == "urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount");
|
||||
- REQUIRE(modules.at(5).name() == "ietf-yang-structure-ext");
|
||||
- REQUIRE(modules.at(5).ns() == "urn:ietf:params:xml:ns:yang:ietf-yang-structure-ext");
|
||||
- REQUIRE(modules.at(6).name() == "mod1");
|
||||
- REQUIRE(modules.at(6).ns() == "http://example.com");
|
||||
- REQUIRE(*modules.at(6).revision() == "2021-11-15");
|
||||
- REQUIRE(modules.at(7).name() == "test");
|
||||
+ REQUIRE(modules.at(2).name() == "default");
|
||||
+ REQUIRE(modules.at(2).ns() == "urn:ietf:params:xml:ns:netconf:default:1.0");
|
||||
+ REQUIRE(modules.at(3).name() == "ietf-inet-types");
|
||||
+ REQUIRE(modules.at(3).ns() == "urn:ietf:params:xml:ns:yang:ietf-inet-types");
|
||||
+ REQUIRE(modules.at(4).name() == "ietf-yang-types");
|
||||
+ REQUIRE(modules.at(4).ns() == "urn:ietf:params:xml:ns:yang:ietf-yang-types");
|
||||
+ REQUIRE(modules.at(5).name() == "ietf-yang-schema-mount");
|
||||
+ REQUIRE(modules.at(5).ns() == "urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount");
|
||||
+ REQUIRE(modules.at(6).name() == "ietf-yang-structure-ext");
|
||||
+ REQUIRE(modules.at(6).ns() == "urn:ietf:params:xml:ns:yang:ietf-yang-structure-ext");
|
||||
+ REQUIRE(modules.at(7).name() == "mod1");
|
||||
REQUIRE(modules.at(7).ns() == "http://example.com");
|
||||
- REQUIRE(modules.at(7).revision() == std::nullopt);
|
||||
+ REQUIRE(*modules.at(7).revision() == "2021-11-15");
|
||||
+ REQUIRE(modules.at(8).name() == "test");
|
||||
+ REQUIRE(modules.at(8).ns() == "http://example.com");
|
||||
+ REQUIRE(modules.at(8).revision() == std::nullopt);
|
||||
}
|
||||
|
||||
DOCTEST_SUBCASE("Module comparison")
|
||||
@@ -704,7 +706,7 @@ TEST_CASE("context")
|
||||
|
||||
DOCTEST_SUBCASE("schema printing")
|
||||
{
|
||||
- std::optional<libyang::Context> ctx_pp{std::in_place, std::nullopt, libyang::ContextOptions::NoYangLibrary | libyang::ContextOptions::DisableSearchCwd | libyang::ContextOptions::SetPrivParsed};
|
||||
+ std::optional<libyang::Context> ctx_pp{std::in_place, std::nullopt, libyang::ContextOptions::NoYangLibrary | libyang::ContextOptions::DisableSearchCwd | libyang::ContextOptions::SetPrivParsed | libyang::ContextOptions::CompileObsolete};
|
||||
auto mod = ctx_pp->parseModule(type_module, libyang::SchemaFormat::YANG);
|
||||
|
||||
REQUIRE(mod.printStr(libyang::SchemaOutputFormat::Tree) == R"(module: type_module
|
||||
diff --git a/tests/data_node.cpp b/tests/data_node.cpp
|
||||
index 9215b12..88ca5b9 100644
|
||||
--- a/tests/data_node.cpp
|
||||
+++ b/tests/data_node.cpp
|
||||
@@ -2426,7 +2426,7 @@ TEST_CASE("Data Node manipulation")
|
||||
"WTF": "foo bar baz"
|
||||
}
|
||||
}
|
||||
- )", libyang::DataFormat::JSON, libyang::OperationType::RpcRestconf),
|
||||
+ )", libyang::DataFormat::JSON, libyang::OperationType::RpcRestconf, libyang::ParseOptions::Strict),
|
||||
"Can't parse into operation data tree: LY_EVALID", libyang::Error);
|
||||
}
|
||||
}
|
||||
diff --git a/tests/schema_node.cpp b/tests/schema_node.cpp
|
||||
index 65ef462..58152a4 100644
|
||||
--- a/tests/schema_node.cpp
|
||||
+++ b/tests/schema_node.cpp
|
||||
@@ -19,7 +19,7 @@ using namespace std::string_literals;
|
||||
TEST_CASE("SchemaNode")
|
||||
{
|
||||
std::optional<libyang::Context> ctx{std::in_place, std::nullopt,
|
||||
- libyang::ContextOptions::NoYangLibrary | libyang::ContextOptions::DisableSearchCwd};
|
||||
+ libyang::ContextOptions::NoYangLibrary | libyang::ContextOptions::DisableSearchCwd | libyang::ContextOptions::CompileObsolete};
|
||||
std::optional<libyang::Context> ctxWithParsed{std::in_place, std::nullopt,
|
||||
libyang::ContextOptions::SetPrivParsed | libyang::ContextOptions::NoYangLibrary | libyang::ContextOptions::DisableSearchCwd};
|
||||
ctx->parseModule(example_schema, libyang::SchemaFormat::YANG);
|
||||
@@ -841,7 +841,7 @@ TEST_CASE("SchemaNode")
|
||||
|
||||
DOCTEST_SUBCASE("LeafList::defaultValuesStr")
|
||||
{
|
||||
- REQUIRE(ctx->findPath("/type_module:leafListWithDefault").asLeafList().defaultValuesStr() == std::vector<std::string>{"-1", "512", "1024", "2048"});
|
||||
+ REQUIRE(ctx->findPath("/type_module:leafListWithDefault").asLeafList().defaultValuesStr() == std::vector<std::string>{"-1", "+512", "0x400", "04000"});
|
||||
REQUIRE(ctx->findPath("/type_module:leafListBasic").asLeafList().defaultValuesStr().size() == 0);
|
||||
}
|
||||
|
||||
--
|
||||
2.43.0
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
From 51d7457bd281e34bac92ff8231978766c7784657 Mon Sep 17 00:00:00 2001
|
||||
From: Martin Bohal <mb@mbohal.cz>
|
||||
Date: Tue, 4 Nov 2025 14:26:05 +0100
|
||||
Subject: [PATCH 5/7] Add SchemaNode::isOutput() method
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Organization: Wires
|
||||
|
||||
This commit introduces the `isOutput()` method to the `SchemaNode`
|
||||
class, which checks whether the schema node is part of an output
|
||||
statement subtree.
|
||||
|
||||
It complements an already existing `isInput()` method.
|
||||
|
||||
Change-Id: I8fe75f2e607c0159d8295d685a59d301155ca92e
|
||||
Signed-off-by: Mattias Walström <lazzer@gmail.com>
|
||||
---
|
||||
include/libyang-cpp/SchemaNode.hpp | 1 +
|
||||
src/SchemaNode.cpp | 10 ++++++++++
|
||||
tests/schema_node.cpp | 7 +++++++
|
||||
3 files changed, 18 insertions(+)
|
||||
|
||||
diff --git a/include/libyang-cpp/SchemaNode.hpp b/include/libyang-cpp/SchemaNode.hpp
|
||||
index 9f49fd7..db5a63e 100644
|
||||
--- a/include/libyang-cpp/SchemaNode.hpp
|
||||
+++ b/include/libyang-cpp/SchemaNode.hpp
|
||||
@@ -58,6 +58,7 @@ public:
|
||||
Status status() const;
|
||||
Config config() const;
|
||||
bool isInput() const;
|
||||
+ bool isOutput() const;
|
||||
NodeType nodeType() const;
|
||||
// It is possible to cast SchemaNode to another type via the following methods. The types are children classes of
|
||||
// SchemaNode. No problems with slicing can occur, because these types are value-based and aren't constructible
|
||||
diff --git a/src/SchemaNode.cpp b/src/SchemaNode.cpp
|
||||
index 2e0351a..9241b23 100644
|
||||
--- a/src/SchemaNode.cpp
|
||||
+++ b/src/SchemaNode.cpp
|
||||
@@ -193,6 +193,16 @@ bool SchemaNode::isInput() const
|
||||
return m_node->flags & LYS_INPUT;
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * @brief Checks whether this node is inside a subtree of an output statement.
|
||||
+ *
|
||||
+ * Wraps `LYS_OUTPUT`.
|
||||
+ */
|
||||
+bool SchemaNode::isOutput() const
|
||||
+{
|
||||
+ return m_node->flags & LYS_OUTPUT;
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* Returns the node type of this node (e.g. leaf, container...).
|
||||
*
|
||||
diff --git a/tests/schema_node.cpp b/tests/schema_node.cpp
|
||||
index 58152a4..01c2f19 100644
|
||||
--- a/tests/schema_node.cpp
|
||||
+++ b/tests/schema_node.cpp
|
||||
@@ -135,6 +135,13 @@ TEST_CASE("SchemaNode")
|
||||
REQUIRE(!ctx->findPath("/type_module:leafString").isInput());
|
||||
}
|
||||
|
||||
+ DOCTEST_SUBCASE("SchemaNode::isOutput")
|
||||
+ {
|
||||
+ REQUIRE(ctx->findPath("/example-schema:myRpc/outputLeaf", libyang::InputOutputNodes::Output).isOutput());
|
||||
+ REQUIRE(!ctx->findPath("/example-schema:myRpc/inputLeaf").isOutput());
|
||||
+ REQUIRE(!ctx->findPath("/type_module:leafString").isOutput());
|
||||
+ }
|
||||
+
|
||||
DOCTEST_SUBCASE("SchemaNode::module")
|
||||
{
|
||||
REQUIRE(ctx->findPath("/type_module:leafString").module().name() == "type_module");
|
||||
--
|
||||
2.43.0
|
||||
|
||||
@@ -0,0 +1,364 @@
|
||||
From 38d5c10009df602594e1b9a53bac41111a9ba430 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= <jan.kundrat@cesnet.cz>
|
||||
Date: Thu, 20 Nov 2025 15:32:03 +0100
|
||||
Subject: [PATCH 6/7] adapt to changes in libyang v4.2
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Organization: Wires
|
||||
|
||||
I was *so* tempted to leave the C++ enums as-is, just to save us from
|
||||
this needless sed exercise, but hey, let's not start confusing any
|
||||
possible downstream consumers now that upstream has changed these
|
||||
identifiers.
|
||||
|
||||
Change-Id: I9b34585822b41be0ca1578051f1c0eb8588a0a51
|
||||
Signed-off-by: Mattias Walström <lazzer@gmail.com>
|
||||
---
|
||||
CMakeLists.txt | 4 +--
|
||||
README.md | 2 +-
|
||||
include/libyang-cpp/Enum.hpp | 4 +--
|
||||
src/utils/enum.hpp | 4 +--
|
||||
tests/context.cpp | 10 +++----
|
||||
tests/data_node.cpp | 58 ++++++++++++++++++------------------
|
||||
6 files changed, 41 insertions(+), 41 deletions(-)
|
||||
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index 90f9aaa..cbaf82a 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -20,7 +20,7 @@ add_custom_target(libyang-cpp-version-cmake
|
||||
cmake/ProjectGitVersionRunner.cmake
|
||||
)
|
||||
include(cmake/ProjectGitVersion.cmake)
|
||||
-set(LIBYANG_CPP_PKG_VERSION "5")
|
||||
+set(LIBYANG_CPP_PKG_VERSION "6")
|
||||
prepare_git_version(LIBYANG_CPP_VERSION ${LIBYANG_CPP_PKG_VERSION})
|
||||
|
||||
find_package(Doxygen)
|
||||
@@ -28,7 +28,7 @@ option(WITH_DOCS "Create and install internal documentation (needs Doxygen)" ${D
|
||||
option(BUILD_SHARED_LIBS "By default, shared libs are enabled. Turn off for a static build." ON)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
-pkg_check_modules(LIBYANG REQUIRED libyang>=4.1.0 IMPORTED_TARGET)
|
||||
+pkg_check_modules(LIBYANG REQUIRED libyang>=4.2.1 IMPORTED_TARGET)
|
||||
|
||||
# FIXME from gcc 14.1 on we should be able to use the calendar/time from libstdc++ and thus remove the date dependency
|
||||
find_package(date)
|
||||
diff --git a/README.md b/README.md
|
||||
index 0cef2ff..842573f 100644
|
||||
--- a/README.md
|
||||
+++ b/README.md
|
||||
@@ -8,7 +8,7 @@
|
||||
Object lifetimes are managed automatically via RAII.
|
||||
|
||||
## Dependencies
|
||||
-- [libyang v4](https://github.com/CESNET/libyang) - the `devel` branch (even for the `master` branch of *libyang-cpp*)
|
||||
+- [libyang v4.2+](https://github.com/CESNET/libyang) - the `devel` branch (even for the `master` branch of *libyang-cpp*)
|
||||
- C++20 compiler (e.g., GCC 10.x+, clang 10+)
|
||||
- CMake 3.19+
|
||||
- optionally for built-in tests, [Doctest](https://github.com/doctest/doctest/) as a C++ unit test framework
|
||||
diff --git a/include/libyang-cpp/Enum.hpp b/include/libyang-cpp/Enum.hpp
|
||||
index 32142f9..096e030 100644
|
||||
--- a/include/libyang-cpp/Enum.hpp
|
||||
+++ b/include/libyang-cpp/Enum.hpp
|
||||
@@ -67,9 +67,9 @@ enum class OperationType : uint32_t {
|
||||
*/
|
||||
enum class PrintFlags : uint32_t {
|
||||
WithDefaultsExplicit = 0x00,
|
||||
- WithSiblings = 0x01,
|
||||
+ Siblings = 0x01,
|
||||
Shrink = 0x02,
|
||||
- KeepEmptyCont = 0x04,
|
||||
+ EmptyContainers = 0x04,
|
||||
WithDefaultsTrim = 0x10,
|
||||
WithDefaultsAll = 0x20,
|
||||
WithDefaultsAllTag = 0x40,
|
||||
diff --git a/src/utils/enum.hpp b/src/utils/enum.hpp
|
||||
index 0b2e2d7..a8902ff 100644
|
||||
--- a/src/utils/enum.hpp
|
||||
+++ b/src/utils/enum.hpp
|
||||
@@ -33,7 +33,7 @@ constexpr uint32_t toPrintFlags(const PrintFlags flags)
|
||||
}
|
||||
// These tests ensure that I used the right numbers when defining my enum.
|
||||
// TODO: add asserts for operator|(PrintFlags, PrintFlags)
|
||||
-static_assert(LYD_PRINT_KEEPEMPTYCONT == toPrintFlags(PrintFlags::KeepEmptyCont));
|
||||
+static_assert(LYD_PRINT_EMPTY_CONT == toPrintFlags(PrintFlags::EmptyContainers));
|
||||
static_assert(LYD_PRINT_SHRINK == toPrintFlags(PrintFlags::Shrink));
|
||||
static_assert(LYD_PRINT_WD_ALL == toPrintFlags(PrintFlags::WithDefaultsAll));
|
||||
static_assert(LYD_PRINT_WD_ALL_TAG == toPrintFlags(PrintFlags::WithDefaultsAllTag));
|
||||
@@ -41,7 +41,7 @@ static_assert(LYD_PRINT_WD_EXPLICIT == toPrintFlags(PrintFlags::WithDefaultsExpl
|
||||
static_assert(LYD_PRINT_WD_IMPL_TAG == toPrintFlags(PrintFlags::WithDefaultsImplicitTag));
|
||||
static_assert(LYD_PRINT_WD_MASK == toPrintFlags(PrintFlags::WithDefaultsMask));
|
||||
static_assert(LYD_PRINT_WD_TRIM == toPrintFlags(PrintFlags::WithDefaultsTrim));
|
||||
-static_assert(LYD_PRINT_WITHSIBLINGS == toPrintFlags(PrintFlags::WithSiblings));
|
||||
+static_assert(LYD_PRINT_SIBLINGS == toPrintFlags(PrintFlags::Siblings));
|
||||
|
||||
#ifndef _MSC_VER
|
||||
// MSVC doesn't respect the underlying enum size
|
||||
diff --git a/tests/context.cpp b/tests/context.cpp
|
||||
index eaedebf..355860b 100644
|
||||
--- a/tests/context.cpp
|
||||
+++ b/tests/context.cpp
|
||||
@@ -502,7 +502,7 @@ TEST_CASE("context")
|
||||
auto errorsNode = node->findXPath("/ietf-restconf:errors");
|
||||
REQUIRE(errorsNode.size() == 1);
|
||||
REQUIRE(errorsNode.begin()->path() == "/ietf-restconf:errors");
|
||||
- REQUIRE(*errorsNode.begin()->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings | libyang::PrintFlags::KeepEmptyCont) == R"({
|
||||
+ REQUIRE(*errorsNode.begin()->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings | libyang::PrintFlags::EmptyContainers) == R"({
|
||||
"ietf-restconf:errors": {
|
||||
"error": [
|
||||
{
|
||||
@@ -589,7 +589,7 @@ TEST_CASE("context")
|
||||
|
||||
auto firstValue = edits.begin()->findPath("value");
|
||||
REQUIRE(firstValue);
|
||||
- REQUIRE(*firstValue->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::KeepEmptyCont) == R"({
|
||||
+ REQUIRE(*firstValue->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::EmptyContainers) == R"({
|
||||
"ietf-yang-patch:value": {
|
||||
"example-schema:person": {
|
||||
"name": "John"
|
||||
@@ -597,7 +597,7 @@ TEST_CASE("context")
|
||||
}
|
||||
}
|
||||
)");
|
||||
- REQUIRE(*firstValue->printStr(libyang::DataFormat::XML, libyang::PrintFlags::KeepEmptyCont) == R"(<value xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
|
||||
+ REQUIRE(*firstValue->printStr(libyang::DataFormat::XML, libyang::PrintFlags::EmptyContainers) == R"(<value xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-patch">
|
||||
<person xmlns="http://example.com/coze">
|
||||
<name>John</name>
|
||||
</person>
|
||||
@@ -607,8 +607,8 @@ TEST_CASE("context")
|
||||
auto secondValueNode = (edits.begin() + 1)->findPath("value");
|
||||
REQUIRE(secondValueNode);
|
||||
auto secondValue = std::get<libyang::DataNode>(secondValueNode->asAny().releaseValue().value());
|
||||
- REQUIRE(*secondValue.printStr(libyang::DataFormat::JSON, libyang::PrintFlags::KeepEmptyCont) == "{\n \"example-schema:dummy\": \"I am a dummy\"\n}\n");
|
||||
- REQUIRE(*secondValue.printStr(libyang::DataFormat::XML, libyang::PrintFlags::KeepEmptyCont) == "<dummy xmlns=\"http://example.com/coze\">I am a dummy</dummy>\n");
|
||||
+ REQUIRE(*secondValue.printStr(libyang::DataFormat::JSON, libyang::PrintFlags::EmptyContainers) == "{\n \"example-schema:dummy\": \"I am a dummy\"\n}\n");
|
||||
+ REQUIRE(*secondValue.printStr(libyang::DataFormat::XML, libyang::PrintFlags::EmptyContainers) == "<dummy xmlns=\"http://example.com/coze\">I am a dummy</dummy>\n");
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/tests/data_node.cpp b/tests/data_node.cpp
|
||||
index 88ca5b9..3f874de 100644
|
||||
--- a/tests/data_node.cpp
|
||||
+++ b/tests/data_node.cpp
|
||||
@@ -137,7 +137,7 @@ TEST_CASE("Data Node manipulation")
|
||||
DOCTEST_SUBCASE("Printing")
|
||||
{
|
||||
auto node = ctx.parseData(data, libyang::DataFormat::JSON);
|
||||
- auto str = node->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings | libyang::PrintFlags::KeepEmptyCont);
|
||||
+ auto str = node->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings | libyang::PrintFlags::EmptyContainers);
|
||||
const auto expected = R"({
|
||||
"example-schema:leafInt32": 420,
|
||||
"example-schema:first": {
|
||||
@@ -158,7 +158,7 @@ TEST_CASE("Data Node manipulation")
|
||||
REQUIRE(str == expected);
|
||||
|
||||
auto emptyCont = ctx.newPath("/example-schema:first");
|
||||
- REQUIRE(emptyCont.printStr(libyang::DataFormat::XML, libyang::PrintFlags::WithSiblings) == std::nullopt);
|
||||
+ REQUIRE(emptyCont.printStr(libyang::DataFormat::XML, libyang::PrintFlags::Siblings) == std::nullopt);
|
||||
}
|
||||
|
||||
DOCTEST_SUBCASE("Overwriting a tree with a different tree")
|
||||
@@ -504,7 +504,7 @@ TEST_CASE("Data Node manipulation")
|
||||
{
|
||||
auto node = std::optional{ctx.newPath("/example-schema:leafInt32", "420")};
|
||||
libyang::validateAll(node, libyang::ValidationOptions::NoState);
|
||||
- auto str = node->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings | libyang::PrintFlags::KeepEmptyCont);
|
||||
+ auto str = node->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings | libyang::PrintFlags::EmptyContainers);
|
||||
REQUIRE(str == data);
|
||||
}
|
||||
|
||||
@@ -725,7 +725,7 @@ TEST_CASE("Data Node manipulation")
|
||||
}
|
||||
|
||||
// The original tree should still be accesible.
|
||||
- node->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings);
|
||||
+ node->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -855,7 +855,7 @@ TEST_CASE("Data Node manipulation")
|
||||
auto cont = ctx.newPath2("/example-schema2:contWithTwoNodes").createdNode;
|
||||
data->unlinkWithSiblings();
|
||||
cont->insertChild(*data);
|
||||
- REQUIRE(*cont->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings) == R"({
|
||||
+ REQUIRE(*cont->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings) == R"({
|
||||
"example-schema2:contWithTwoNodes": {
|
||||
"one": 333,
|
||||
"two": 666
|
||||
@@ -1501,9 +1501,9 @@ TEST_CASE("Data Node manipulation")
|
||||
REQUIRE(std::holds_alternative<libyang::DataNode>(rawVal));
|
||||
auto retrieved = std::get<libyang::DataNode>(rawVal);
|
||||
REQUIRE(retrieved.path() == "/key");
|
||||
- REQUIRE(*retrieved.printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Shrink | libyang::PrintFlags::WithSiblings)
|
||||
+ REQUIRE(*retrieved.printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Shrink | libyang::PrintFlags::Siblings)
|
||||
== R"|({"key":"value"})|");
|
||||
- REQUIRE(*retrieved.printStr(libyang::DataFormat::XML, libyang::PrintFlags::Shrink | libyang::PrintFlags::WithSiblings)
|
||||
+ REQUIRE(*retrieved.printStr(libyang::DataFormat::XML, libyang::PrintFlags::Shrink | libyang::PrintFlags::Siblings)
|
||||
== R"|(<key>value</key>)|");
|
||||
}
|
||||
}
|
||||
@@ -1525,9 +1525,9 @@ TEST_CASE("Data Node manipulation")
|
||||
REQUIRE(std::holds_alternative<libyang::DataNode>(rawVal));
|
||||
auto retrieved = std::get<libyang::DataNode>(rawVal);
|
||||
REQUIRE(retrieved.path() == "/something");
|
||||
- REQUIRE(*retrieved.printStr(libyang::DataFormat::XML, libyang::PrintFlags::Shrink | libyang::PrintFlags::WithSiblings)
|
||||
+ REQUIRE(*retrieved.printStr(libyang::DataFormat::XML, libyang::PrintFlags::Shrink | libyang::PrintFlags::Siblings)
|
||||
== R"|(<something>lol</something>)|");
|
||||
- REQUIRE(*retrieved.printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Shrink | libyang::PrintFlags::WithSiblings)
|
||||
+ REQUIRE(*retrieved.printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Shrink | libyang::PrintFlags::Siblings)
|
||||
== R"|({"something":"lol"})|");
|
||||
}
|
||||
}
|
||||
@@ -1572,9 +1572,9 @@ TEST_CASE("Data Node manipulation")
|
||||
val = jsonAnyXmlNode.createdNode->asAny().releaseValue();
|
||||
}
|
||||
|
||||
- REQUIRE(*jsonAnyXmlNode.createdNode->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Shrink | libyang::PrintFlags::WithSiblings)
|
||||
+ REQUIRE(*jsonAnyXmlNode.createdNode->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Shrink | libyang::PrintFlags::Siblings)
|
||||
== R"|({"example-schema:ax":[1,2,3]})|"s);
|
||||
- REQUIRE(*jsonAnyXmlNode.createdNode->printStr(libyang::DataFormat::XML, libyang::PrintFlags::Shrink | libyang::PrintFlags::WithSiblings)
|
||||
+ REQUIRE(*jsonAnyXmlNode.createdNode->printStr(libyang::DataFormat::XML, libyang::PrintFlags::Shrink | libyang::PrintFlags::Siblings)
|
||||
== R"|(<ax xmlns="http://example.com/coze">)|"s + origJSON + "</ax>");
|
||||
}
|
||||
|
||||
@@ -1603,9 +1603,9 @@ TEST_CASE("Data Node manipulation")
|
||||
}
|
||||
|
||||
REQUIRE(root);
|
||||
- REQUIRE(*root->printStr(libyang::DataFormat::XML, libyang::PrintFlags::Shrink | libyang::PrintFlags::WithSiblings)
|
||||
+ REQUIRE(*root->printStr(libyang::DataFormat::XML, libyang::PrintFlags::Shrink | libyang::PrintFlags::Siblings)
|
||||
== origXML);
|
||||
- REQUIRE(*root->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Shrink | libyang::PrintFlags::WithSiblings)
|
||||
+ REQUIRE(*root->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Shrink | libyang::PrintFlags::Siblings)
|
||||
== origJSON);
|
||||
|
||||
auto node = root->findPath("/example-schema:ax");
|
||||
@@ -1665,9 +1665,9 @@ TEST_CASE("Data Node manipulation")
|
||||
auto retrieved = std::get<libyang::DataNode>(*val);
|
||||
val.reset();
|
||||
REQUIRE(retrieved.path() == "/a");
|
||||
- REQUIRE(*retrieved.printStr(libyang::DataFormat::XML, libyang::PrintFlags::Shrink | libyang::PrintFlags::WithSiblings)
|
||||
+ REQUIRE(*retrieved.printStr(libyang::DataFormat::XML, libyang::PrintFlags::Shrink | libyang::PrintFlags::Siblings)
|
||||
== origXML);
|
||||
- REQUIRE(*retrieved.printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Shrink | libyang::PrintFlags::WithSiblings)
|
||||
+ REQUIRE(*retrieved.printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Shrink | libyang::PrintFlags::Siblings)
|
||||
== origJSON);
|
||||
}
|
||||
}
|
||||
@@ -1806,7 +1806,7 @@ TEST_CASE("Data Node manipulation")
|
||||
|
||||
nodeX.parseSubtree(data, libyang::DataFormat::JSON,
|
||||
libyang::ParseOptions::Strict | libyang::ParseOptions::NoState | libyang::ParseOptions::ParseOnly);
|
||||
- REQUIRE(*nodeX.printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings) == R"({
|
||||
+ REQUIRE(*nodeX.printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings) == R"({
|
||||
"example-schema5:x": {
|
||||
"x_b": {
|
||||
"x_b_leaf": 666
|
||||
@@ -1948,7 +1948,7 @@ TEST_CASE("Data Node manipulation")
|
||||
{
|
||||
netconfDeletePresenceCont.newMeta(netconf, "operation", "delete");
|
||||
netconfDeletePresenceCont.newMeta(ietfOrigin, "origin", "ietf-origin:default");
|
||||
- REQUIRE(*netconfDeletePresenceCont.printStr(libyang::DataFormat::XML, libyang::PrintFlags::WithSiblings)
|
||||
+ REQUIRE(*netconfDeletePresenceCont.printStr(libyang::DataFormat::XML, libyang::PrintFlags::Siblings)
|
||||
== R"(<presenceContainer xmlns="http://example.com/coze" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" nc:operation="delete" xmlns:or="urn:ietf:params:xml:ns:yang:ietf-origin" or:origin="or:default"/>)" "\n");
|
||||
}
|
||||
|
||||
@@ -1957,7 +1957,7 @@ TEST_CASE("Data Node manipulation")
|
||||
auto opaqueLeaf = ctx.newPath("/example-schema:leafInt32", std::nullopt, libyang::CreationOptions::Opaque);
|
||||
REQUIRE_THROWS(opaqueLeaf.newMeta(netconf, "operation", "delete"));
|
||||
opaqueLeaf.newAttrOpaqueJSON("ietf-netconf", "operation", "delete");
|
||||
- REQUIRE(*opaqueLeaf.printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings)
|
||||
+ REQUIRE(*opaqueLeaf.printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings)
|
||||
== R"({
|
||||
"example-schema:leafInt32": "",
|
||||
"@example-schema:leafInt32": {
|
||||
@@ -1976,7 +1976,7 @@ TEST_CASE("Data Node manipulation")
|
||||
auto discard2 = ctx.newOpaqueJSON(libyang::OpaqueName{"sysrepo", "sysrepo", "discard-items"}, libyang::JSON{"/example-schema:b"});
|
||||
REQUIRE(!!discard2);
|
||||
discard1->insertSibling(*discard2);
|
||||
- REQUIRE(*discard1->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings)
|
||||
+ REQUIRE(*discard1->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings)
|
||||
== R"({
|
||||
"sysrepo:discard-items": "/example-schema:a",
|
||||
"sysrepo:discard-items": "/example-schema:b"
|
||||
@@ -1991,7 +1991,7 @@ TEST_CASE("Data Node manipulation")
|
||||
|
||||
auto leafInt16 = ctx.newPath("/example-schema:leafInt16", "666");
|
||||
leafInt16.insertSibling(*discard1);
|
||||
- REQUIRE(*discard1->firstSibling().printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings)
|
||||
+ REQUIRE(*discard1->firstSibling().printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings)
|
||||
== R"({
|
||||
"example-schema:leafInt16": 666,
|
||||
"sysrepo:discard-items": "/example-schema:a",
|
||||
@@ -2020,7 +2020,7 @@ TEST_CASE("Data Node manipulation")
|
||||
|
||||
dummy.insertSibling(*discard3);
|
||||
leafInt16.insertSibling(dummy);
|
||||
- REQUIRE(*discard1->firstSibling().printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings)
|
||||
+ REQUIRE(*discard1->firstSibling().printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings)
|
||||
== R"({
|
||||
"example-schema:dummy": "blah",
|
||||
"example-schema:leafInt16": 666,
|
||||
@@ -2112,8 +2112,8 @@ TEST_CASE("Data Node manipulation")
|
||||
data->unlinkWithSiblings();
|
||||
out->insertChild(*data);
|
||||
|
||||
- REQUIRE(*out->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings) == expectedJson);
|
||||
- REQUIRE(*out->printStr(libyang::DataFormat::XML, libyang::PrintFlags::WithSiblings) == expectedXml);
|
||||
+ REQUIRE(*out->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings) == expectedJson);
|
||||
+ REQUIRE(*out->printStr(libyang::DataFormat::XML, libyang::PrintFlags::Siblings) == expectedXml);
|
||||
}
|
||||
|
||||
DOCTEST_SUBCASE("libyang internal metadata")
|
||||
@@ -2148,7 +2148,7 @@ TEST_CASE("Data Node manipulation")
|
||||
auto node = ctx.newExtPath(ext, "/ietf-restconf:errors", std::nullopt, std::nullopt);
|
||||
REQUIRE(node);
|
||||
REQUIRE(node->schema().name() == "errors");
|
||||
- REQUIRE(*node->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings | libyang::PrintFlags::KeepEmptyCont) == R"({
|
||||
+ REQUIRE(*node->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings | libyang::PrintFlags::EmptyContainers) == R"({
|
||||
"ietf-restconf:errors": {}
|
||||
}
|
||||
)");
|
||||
@@ -2157,7 +2157,7 @@ TEST_CASE("Data Node manipulation")
|
||||
REQUIRE(node->newPath("ietf-restconf:error[1]/error-tag", "invalid-attribute"));
|
||||
REQUIRE(node->newExtPath(ext, "/ietf-restconf:errors/error[1]/error-message", "ahoj"));
|
||||
REQUIRE_THROWS_WITH(node->newPath("ietf-restconf:error[1]/error-message", "duplicate create"), "Couldn't create a node with path 'ietf-restconf:error[1]/error-message': LY_EEXIST");
|
||||
- REQUIRE(*node->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings | libyang::PrintFlags::KeepEmptyCont) == R"({
|
||||
+ REQUIRE(*node->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings | libyang::PrintFlags::EmptyContainers) == R"({
|
||||
"ietf-restconf:errors": {
|
||||
"error": [
|
||||
{
|
||||
@@ -2173,7 +2173,7 @@ TEST_CASE("Data Node manipulation")
|
||||
REQUIRE(node->newExtPath(ext, "/ietf-restconf:errors/error[2]/error-type", "transport"));
|
||||
REQUIRE(node->newExtPath(ext, "/ietf-restconf:errors/error[2]/error-tag", "invalid-attribute"));
|
||||
REQUIRE(node->newPath("ietf-restconf:error[2]/error-message", "aaa"));
|
||||
- REQUIRE(*node->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings | libyang::PrintFlags::KeepEmptyCont) == R"({
|
||||
+ REQUIRE(*node->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings | libyang::PrintFlags::EmptyContainers) == R"({
|
||||
"ietf-restconf:errors": {
|
||||
"error": [
|
||||
{
|
||||
@@ -2397,8 +2397,8 @@ TEST_CASE("Data Node manipulation")
|
||||
REQUIRE(response.tree->path() == "/example-schema:output");
|
||||
REQUIRE(response.tree->isOpaque());
|
||||
REQUIRE(!response.tree->child()); // nothing gets "parsed" here, the result is put into the tree that parseOp() operated on (!)
|
||||
- CAPTURE(*response.tree->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings | libyang::PrintFlags::KeepEmptyCont));
|
||||
- CAPTURE(*replyTree.printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings | libyang::PrintFlags::KeepEmptyCont));
|
||||
+ CAPTURE(*response.tree->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings | libyang::PrintFlags::EmptyContainers));
|
||||
+ CAPTURE(*replyTree.printStr(libyang::DataFormat::JSON, libyang::PrintFlags::Siblings | libyang::PrintFlags::EmptyContainers));
|
||||
|
||||
node = replyTree.findPath("/example-schema:myRpc/outputLeaf", libyang::InputOutputNodes::Output);
|
||||
REQUIRE(!!node);
|
||||
@@ -2472,7 +2472,7 @@ TEST_CASE("Data Node manipulation")
|
||||
REQUIRE(rpcOp.tree);
|
||||
|
||||
libyang::validateOp(*rpcTree, depTree, libyang::OperationType::RpcRestconf);
|
||||
- REQUIRE(*rpcTree->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::KeepEmptyCont) == expected);
|
||||
+ REQUIRE(*rpcTree->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::EmptyContainers) == expected);
|
||||
}
|
||||
|
||||
DOCTEST_SUBCASE("Nodes in disjunctive cases defined together")
|
||||
--
|
||||
2.43.0
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
From af187d1bc27a7390b358d7ad3b9f8f44cabe41ad Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= <jan.kundrat@cesnet.cz>
|
||||
Date: Thu, 20 Nov 2025 12:46:49 +0100
|
||||
Subject: [PATCH 7/7] don't segfault when listing module's features
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Organization: Wires
|
||||
|
||||
This is especially relevant when working with the "printed context" from
|
||||
sysrepo, because that context has all the parsed info removed. As a
|
||||
result, it is not possible to query a module's list of features. Let's
|
||||
stop dereferencing a null pointer in that case.
|
||||
|
||||
Bug: https://github.com/sysrepo/sysrepo/issues/3695
|
||||
Change-Id: I6258f580b4e5aa02cae2d60260b84593aefcf587
|
||||
Signed-off-by: Mattias Walström <lazzer@gmail.com>
|
||||
---
|
||||
CMakeLists.txt | 1 +
|
||||
include/libyang-cpp/Utils.hpp | 1 +
|
||||
src/Module.cpp | 3 +++
|
||||
src/utils/exception.cpp | 5 +++++
|
||||
tests/context.cpp | 12 ++++++++++++
|
||||
5 files changed, 22 insertions(+)
|
||||
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index cbaf82a..b9da66b 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -118,6 +118,7 @@ if(BUILD_TESTING)
|
||||
endfunction()
|
||||
|
||||
libyang_cpp_test(context)
|
||||
+ target_link_libraries(test_context PkgConfig::LIBYANG)
|
||||
libyang_cpp_test(data_node)
|
||||
target_link_libraries(test_data_node PkgConfig::LIBYANG)
|
||||
libyang_cpp_test(schema_node)
|
||||
diff --git a/include/libyang-cpp/Utils.hpp b/include/libyang-cpp/Utils.hpp
|
||||
index 934714d..428b265 100644
|
||||
--- a/include/libyang-cpp/Utils.hpp
|
||||
+++ b/include/libyang-cpp/Utils.hpp
|
||||
@@ -35,6 +35,7 @@ public:
|
||||
class LIBYANG_CPP_EXPORT ParsedInfoUnavailable : public Error {
|
||||
public:
|
||||
explicit ParsedInfoUnavailable();
|
||||
+ explicit ParsedInfoUnavailable(const std::string& what);
|
||||
};
|
||||
|
||||
/**
|
||||
diff --git a/src/Module.cpp b/src/Module.cpp
|
||||
index d6d4023..8eb8fb4 100644
|
||||
--- a/src/Module.cpp
|
||||
+++ b/src/Module.cpp
|
||||
@@ -155,6 +155,9 @@ void Module::setImplemented(const AllFeatures)
|
||||
*/
|
||||
std::vector<Feature> Module::features() const
|
||||
{
|
||||
+ if (!m_module->parsed) {
|
||||
+ throw ParsedInfoUnavailable{"Module::features: lys_module::parsed is not available"};
|
||||
+ }
|
||||
std::vector<Feature> res;
|
||||
for (const auto& feature : std::span(m_module->parsed->features, LY_ARRAY_COUNT(m_module->parsed->features))) {
|
||||
res.emplace_back(Feature{&feature, m_ctx});
|
||||
diff --git a/src/utils/exception.cpp b/src/utils/exception.cpp
|
||||
index ecd2b85..a8d908b 100644
|
||||
--- a/src/utils/exception.cpp
|
||||
+++ b/src/utils/exception.cpp
|
||||
@@ -22,6 +22,11 @@ ParsedInfoUnavailable::ParsedInfoUnavailable()
|
||||
{
|
||||
}
|
||||
|
||||
+ParsedInfoUnavailable::ParsedInfoUnavailable(const std::string& what)
|
||||
+ : Error(what)
|
||||
+{
|
||||
+}
|
||||
+
|
||||
ErrorCode ErrorWithCode::code() const
|
||||
{
|
||||
return m_errCode;
|
||||
diff --git a/tests/context.cpp b/tests/context.cpp
|
||||
index 355860b..5fce0f4 100644
|
||||
--- a/tests/context.cpp
|
||||
+++ b/tests/context.cpp
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <fstream>
|
||||
#include <libyang-cpp/Context.hpp>
|
||||
#include <libyang-cpp/Utils.hpp>
|
||||
+#include <libyang/context.h>
|
||||
#include "example_schema.hpp"
|
||||
#include "pretty_printers.hpp"
|
||||
#include "test_vars.hpp"
|
||||
@@ -333,6 +334,17 @@ TEST_CASE("context")
|
||||
REQUIRE(enabledFeatures == expectedEnabledFeatures);
|
||||
}
|
||||
|
||||
+ DOCTEST_SUBCASE("printed context")
|
||||
+ {
|
||||
+ ctx->setSearchDir(TESTS_DIR / "yang");
|
||||
+ auto mod = ctx->loadModule("mod1", std::nullopt, {"*"});
|
||||
+ REQUIRE(mod.features().size() == 3);
|
||||
+ ly_ctx_free_parsed(retrieveContext(*ctx));
|
||||
+ REQUIRE_THROWS_WITH_AS(mod.features(),
|
||||
+ "Module::features: lys_module::parsed is not available",
|
||||
+ libyang::ParsedInfoUnavailable);
|
||||
+ }
|
||||
+
|
||||
DOCTEST_SUBCASE("Module::setImplemented")
|
||||
{
|
||||
ctx->setSearchDir(TESTS_DIR / "yang");
|
||||
--
|
||||
2.43.0
|
||||
|
||||
Reference in New Issue
Block a user