| #include <boost/algorithm/string/predicate.hpp> |
| #include <cmath> |
| #include "datastore_access.hpp" |
| #include "libyang_utils.hpp" |
| #include "utils.hpp" |
| |
| leaf_data_ leafValueFromNode(libyang::S_Data_Node_Leaf_List 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); |
| } |
| |
| namespace { |
| void impl_lyNodesToTree(DatastoreAccess::Tree& res, const std::vector<std::shared_ptr<libyang::Data_Node>> 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()) { |
| // 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}); |
| } |
| } |
| if (it->schema()->nodetype() == LYS_LIST) { |
| res.emplace_back(stripXPathPrefix(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); |
| } |
| } |
| } |
| } |
| |
| void lyNodesToTree(DatastoreAccess::Tree& res, const std::vector<std::shared_ptr<libyang::Data_Node>> 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()); |
| res.emplace_back(leafListPath, special_{SpecialValue::LeafList}); |
| while (it != items.end() && boost::starts_with((*it)->path(), leafListPath)) { |
| impl_lyNodesToTree(res, (*it)->tree_dfs(), ignoredXPathPrefix); |
| it++; |
| } |
| } else { |
| impl_lyNodesToTree(res, (*it)->tree_dfs(), ignoredXPathPrefix); |
| } |
| } |
| } |
| |
| DatastoreAccess::Tree rpcOutputToTree(const std::string& rpcPath, libyang::S_Data_Node 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(), "/")); |
| } |
| return res; |
| } |
| |
| libyang::S_Data_Node treeToRpcInput(libyang::S_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); |
| for (const auto& [k, v] : in) { |
| root->new_path(ctx, k.c_str(), leafDataToString(v).c_str(), LYD_ANYDATA_CONSTSTRING, LYD_PATH_OPT_UPDATE); |
| } |
| |
| return root; |
| } |