blob: e18c780000d3f23c3cbe22d078fcb1f26ee67e6f [file] [log] [blame]
Václav Kubernát0d4db442018-07-18 17:18:43 +02001/*
2 * Copyright (C) 2018 CESNET, https://photonics.cesnet.cz/
3 * Copyright (C) 2018 FIT CVUT, https://fit.cvut.cz/
4 *
5 * Written by Václav Kubernát <kubervac@fit.cvut.cz>
6 *
7*/
8
9#include <libyang/Libyang.hpp>
Václav Kubernátc31bd602019-03-07 11:44:48 +010010#include <libyang/Tree_Data.hpp>
Václav Kubernát0d4db442018-07-18 17:18:43 +020011#include <libyang/Tree_Schema.hpp>
12#include <string_view>
Václav Kubernát26b56082020-02-03 18:28:56 +010013#include "UniqueResource.hpp"
Václav Kubernát0d4db442018-07-18 17:18:43 +020014#include "utils.hpp"
15#include "yang_schema.hpp"
16
17class YangLoadError : public std::runtime_error {
18public:
19 using std::runtime_error::runtime_error;
20 ~YangLoadError() override = default;
21};
22
23class UnsupportedYangTypeException : public std::runtime_error {
24public:
25 using std::runtime_error::runtime_error;
26 ~UnsupportedYangTypeException() override = default;
27};
28
29class InvalidSchemaQueryException : public std::runtime_error {
30public:
31 using std::runtime_error::runtime_error;
32 ~InvalidSchemaQueryException() override = default;
33};
34
Václav Kubernát2eaceb82018-10-08 19:56:30 +020035template <typename T>
36std::string pathToYangAbsSchemPath(const T& path)
Václav Kubernát0d4db442018-07-18 17:18:43 +020037{
38 std::string res = "/";
39 std::string currentModule;
40
41 for (const auto& it : path.m_nodes) {
42 const auto name = nodeToSchemaString(it);
43
44 if (it.m_suffix.type() == typeid(module_)) {
45 currentModule = name;
46 continue;
47 } else {
48 res += currentModule + ":";
49 res += name + "/";
50 }
51 }
52
53 return res;
54}
55
56YangSchema::YangSchema()
Václav Kubernáta6c5fff2018-09-07 15:16:25 +020057 : m_context(std::make_shared<libyang::Context>(nullptr, LY_CTX_DISABLE_SEARCHDIRS | LY_CTX_DISABLE_SEARCHDIR_CWD))
Václav Kubernát0d4db442018-07-18 17:18:43 +020058{
Václav Kubernát0d4db442018-07-18 17:18:43 +020059}
60
Václav Kubernát1d50a5b2020-02-03 16:44:22 +010061YangSchema::YangSchema(std::shared_ptr<libyang::Context> lyCtx)
62 : m_context(lyCtx)
63{
64
65}
66
Václav Kubernát0d4db442018-07-18 17:18:43 +020067YangSchema::~YangSchema() = default;
68
69void YangSchema::addSchemaString(const char* schema)
70{
71 if (!m_context->parse_module_mem(schema, LYS_IN_YANG)) {
72 throw YangLoadError("Couldn't load schema");
73 }
74}
75
76void YangSchema::addSchemaDirectory(const char* directoryName)
77{
78 if (m_context->set_searchdir(directoryName)) {
79 throw YangLoadError("Couldn't add schema search directory");
80 }
81}
82
83void YangSchema::addSchemaFile(const char* filename)
84{
85 if (!m_context->parse_module_path(filename, LYS_IN_YANG)) {
86 throw YangLoadError("Couldn't load schema");
87 }
88}
89
Václav Kubernát75877de2019-11-20 17:43:02 +010090bool YangSchema::isModule(const std::string& name) const
Václav Kubernát0d4db442018-07-18 17:18:43 +020091{
92 const auto set = modules();
93 return set.find(name) != set.end();
94}
95
Václav Kubernát2eaceb82018-10-08 19:56:30 +020096bool YangSchema::leafEnumHasValue(const schemaPath_& location, const ModuleNodePair& node, const std::string& value) const
Václav Kubernát0d4db442018-07-18 17:18:43 +020097{
Václav Kubernát989b5de2019-02-20 16:28:35 +010098 auto enums = enumValues(location, node);
99
100 return std::any_of(enums.begin(), enums.end(), [=](const auto& x) { return x == value; });
101}
102
103const std::set<std::string> YangSchema::enumValues(const schemaPath_& location, const ModuleNodePair& node) const
104{
Václav Kubernát0d4db442018-07-18 17:18:43 +0200105 if (!isLeaf(location, node) || leafType(location, node) != yang::LeafDataTypes::Enum)
Václav Kubernát989b5de2019-02-20 16:28:35 +0100106 return {};
Václav Kubernát0d4db442018-07-18 17:18:43 +0200107
Jan Kundrát4ed0e9f2018-08-23 16:56:58 +0200108 libyang::Schema_Node_Leaf leaf(getSchemaNode(location, node));
Václav Kubernát6a713d62018-10-03 18:47:34 +0200109 auto type = leaf.type();
110 auto enm = type->info()->enums()->enm();
111 // The enum can be a derived type and enm() only returns values,
112 // if that specific typedef changed the possible values. So we go
113 // up the hierarchy until we find a typedef that defined these values.
114 while (enm.empty()) {
115 type = type->der()->type();
116 enm = type->info()->enums()->enm();
117 }
Václav Kubernát0d4db442018-07-18 17:18:43 +0200118
Václav Kubernáta38d4172019-11-04 12:36:39 +0100119 std::vector<libyang::S_Type_Enum> enabled;
Václav Kubernát90de9502019-11-20 17:19:44 +0100120 std::copy_if(enm.begin(), enm.end(), std::back_inserter(enabled), [](const libyang::S_Type_Enum& it) {
Václav Kubernáta38d4172019-11-04 12:36:39 +0100121 auto iffeatures = it->iffeature();
Václav Kubernát90de9502019-11-20 17:19:44 +0100122 return std::all_of(iffeatures.begin(), iffeatures.end(), [](auto it) { return it->value(); });
Václav Kubernáta38d4172019-11-04 12:36:39 +0100123 });
124
Václav Kubernát989b5de2019-02-20 16:28:35 +0100125 std::set<std::string> enumSet;
Václav Kubernáta38d4172019-11-04 12:36:39 +0100126 std::transform(enabled.begin(), enabled.end(), std::inserter(enumSet, enumSet.end()), [](auto it) { return it->name(); });
Václav Kubernát989b5de2019-02-20 16:28:35 +0100127 return enumSet;
Václav Kubernát0d4db442018-07-18 17:18:43 +0200128}
129
Václav Kubernáteeb38842019-03-20 19:46:05 +0100130const std::set<std::string> YangSchema::validIdentities(const schemaPath_& location, const ModuleNodePair& node, const Prefixes prefixes) const
131{
132 if (!isLeaf(location, node) || leafType(location, node) != yang::LeafDataTypes::IdentityRef)
133 return {};
134
135 std::set<std::string> identSet;
136
137 auto topLevelModule = location.m_nodes.empty() ? node.first.get() : location.m_nodes.front().m_prefix.get().m_name;
138 auto insertToSet = [&identSet, prefixes, topLevelModule](auto module, auto name) {
139 std::string stringIdent;
140 if (prefixes == Prefixes::Always || topLevelModule != module) {
141 stringIdent += module;
142 stringIdent += ":";
143 }
144 stringIdent += name;
145 identSet.emplace(stringIdent);
146 };
147
148 auto leaf = std::make_shared<libyang::Schema_Node_Leaf>(getSchemaNode(location, node));
149 auto info = leaf->type()->info();
150 for (auto base : info->ident()->ref()) { // Iterate over all bases
151 insertToSet(base->module()->name(), base->name());
152 // Iterate over derived identities (this is recursive!)
153 for (auto derived : base->der()->schema()) {
154 insertToSet(derived->module()->name(), derived->name());
155 }
156 }
157
158 return identSet;
159}
160
161bool YangSchema::leafIdentityIsValid(const schemaPath_& location, const ModuleNodePair& node, const ModuleValuePair& value) const
162{
163 auto identities = validIdentities(location, node, Prefixes::Always);
164
165 auto topLevelModule = location.m_nodes.empty() ? node.first.get() : location.m_nodes.front().m_prefix.get().m_name;
166 auto identModule = value.first ? value.first.value() : topLevelModule;
Václav Kubernát1bf704e2019-04-12 13:30:50 +0200167 return std::any_of(identities.begin(), identities.end(), [toFind = identModule + ":" + value.second](const auto& x) { return x == toFind; });
Václav Kubernáteeb38842019-03-20 19:46:05 +0100168}
169
Václav Kubernát2eaceb82018-10-08 19:56:30 +0200170bool YangSchema::listHasKey(const schemaPath_& location, const ModuleNodePair& node, const std::string& key) const
Václav Kubernát0d4db442018-07-18 17:18:43 +0200171{
172 if (!isList(location, node))
173 return false;
174 const auto keys = listKeys(location, node);
175 return keys.find(key) != keys.end();
176}
177
Václav Kubernátc3866792020-02-20 14:12:56 +0100178bool YangSchema::leafIsKey(const std::string& leafPath) const
179{
180 auto node = getSchemaNode(leafPath);
181 if (!node || node->nodetype() != LYS_LEAF)
182 return false;
183
184 return libyang::Schema_Node_Leaf{node}.is_key().get();
185}
186
Václav Kubernát47a3f672019-11-08 15:42:43 +0100187libyang::S_Schema_Node YangSchema::impl_getSchemaNode(const std::string& node) const
Václav Kubernát0d4db442018-07-18 17:18:43 +0200188{
Václav Kubernátf2b91e02019-04-11 15:36:48 +0200189 // If no node is found find_path prints an error message, so we have to
190 // disable logging
191 // https://github.com/CESNET/libyang/issues/753
192 {
193 int oldOptions;
194 auto logBlocker = make_unique_resource(
195 [&oldOptions]() {
196 oldOptions = libyang::set_log_options(0);
197 },
198 [&oldOptions]() {
199 libyang::set_log_options(oldOptions);
200 });
Václav Kubernát47a3f672019-11-08 15:42:43 +0100201 return m_context->get_node(nullptr, node.c_str());
Václav Kubernátf2b91e02019-04-11 15:36:48 +0200202 }
Václav Kubernát0d4db442018-07-18 17:18:43 +0200203}
204
Václav Kubernát47a3f672019-11-08 15:42:43 +0100205
206libyang::S_Schema_Node YangSchema::getSchemaNode(const std::string& node) const
207{
208 return impl_getSchemaNode(node);
209}
210
211libyang::S_Schema_Node YangSchema::getSchemaNode(const schemaPath_& location, const ModuleNodePair& node) const
212{
Václav Kubernátefcac932020-01-10 15:26:32 +0100213 std::string absPath = joinPaths(pathToSchemaString(location, Prefixes::Always), fullNodeName(location, node));
Václav Kubernát47a3f672019-11-08 15:42:43 +0100214
215 return impl_getSchemaNode(absPath);
216}
217
Václav Kubernát2eaceb82018-10-08 19:56:30 +0200218const std::set<std::string> YangSchema::listKeys(const schemaPath_& location, const ModuleNodePair& node) const
Václav Kubernát0d4db442018-07-18 17:18:43 +0200219{
220 std::set<std::string> keys;
221 if (!isList(location, node))
222 return keys;
Jan Kundrát4ed0e9f2018-08-23 16:56:58 +0200223 libyang::Schema_Node_List list(getSchemaNode(location, node));
Jan Kundrát4030b772018-08-23 15:54:56 +0200224 const auto& keysVec = list.keys();
Václav Kubernát0d4db442018-07-18 17:18:43 +0200225
Václav Kubernát90de9502019-11-20 17:19:44 +0100226 std::transform(keysVec.begin(), keysVec.end(), std::inserter(keys, keys.begin()), [](const auto& it) { return it->name(); });
Václav Kubernát0d4db442018-07-18 17:18:43 +0200227 return keys;
228}
229
Václav Kubernát6a8d1d92019-04-24 20:30:36 +0200230yang::LeafDataTypes lyTypeToLeafDataTypes(LY_DATA_TYPE type)
Václav Kubernát0d4db442018-07-18 17:18:43 +0200231{
Václav Kubernát6a8d1d92019-04-24 20:30:36 +0200232 switch (type) {
Václav Kubernát0d4db442018-07-18 17:18:43 +0200233 case LY_TYPE_STRING:
234 return yang::LeafDataTypes::String;
235 case LY_TYPE_DEC64:
236 return yang::LeafDataTypes::Decimal;
237 case LY_TYPE_BOOL:
238 return yang::LeafDataTypes::Bool;
Ivona Oboňová88c78ca2019-07-02 18:40:07 +0200239 case LY_TYPE_INT8:
240 return yang::LeafDataTypes::Int8;
241 case LY_TYPE_INT16:
242 return yang::LeafDataTypes::Int16;
Václav Kubernát0d4db442018-07-18 17:18:43 +0200243 case LY_TYPE_INT32:
Ivona Oboňová88c78ca2019-07-02 18:40:07 +0200244 return yang::LeafDataTypes::Int32;
245 case LY_TYPE_INT64:
246 return yang::LeafDataTypes::Int64;
247 case LY_TYPE_UINT8:
248 return yang::LeafDataTypes::Uint8;
249 case LY_TYPE_UINT16:
250 return yang::LeafDataTypes::Uint16;
Václav Kubernát0d4db442018-07-18 17:18:43 +0200251 case LY_TYPE_UINT32:
Ivona Oboňová88c78ca2019-07-02 18:40:07 +0200252 return yang::LeafDataTypes::Uint32;
253 case LY_TYPE_UINT64:
254 return yang::LeafDataTypes::Uint64;
Václav Kubernát0d4db442018-07-18 17:18:43 +0200255 case LY_TYPE_ENUM:
256 return yang::LeafDataTypes::Enum;
Václav Kubernátab538992019-03-06 15:30:50 +0100257 case LY_TYPE_BINARY:
258 return yang::LeafDataTypes::Binary;
Václav Kubernáteeb38842019-03-20 19:46:05 +0100259 case LY_TYPE_IDENT:
260 return yang::LeafDataTypes::IdentityRef;
Václav Kubernát6a8d1d92019-04-24 20:30:36 +0200261 case LY_TYPE_LEAFREF:
262 return yang::LeafDataTypes::LeafRef;
Václav Kubernát0d4db442018-07-18 17:18:43 +0200263 default:
Václav Kubernát6a8d1d92019-04-24 20:30:36 +0200264 using namespace std::string_literals;
265 throw std::logic_error{"internal error: unsupported libyang data type "s + std::to_string(type)};
266 }
Václav Kubernát6a8d1d92019-04-24 20:30:36 +0200267}
268
Václav Kubernát9bf36852020-02-18 17:47:56 +0100269namespace {
270yang::LeafDataTypes impl_leafType(const libyang::S_Schema_Node& node)
Václav Kubernát6a8d1d92019-04-24 20:30:36 +0200271{
272 using namespace std::string_literals;
Václav Kubernát9bf36852020-02-18 17:47:56 +0100273 libyang::Schema_Node_Leaf leaf(node);
Václav Kubernát6a8d1d92019-04-24 20:30:36 +0200274 auto baseType{leaf.type()->base()};
275 try {
276 return lyTypeToLeafDataTypes(baseType);
Václav Kubernát90de9502019-11-20 17:19:44 +0100277 } catch (std::logic_error& ex) {
Václav Kubernát9bf36852020-02-18 17:47:56 +0100278 throw UnsupportedYangTypeException("the type of "s + node->name() + " is not supported: " + ex.what());
Václav Kubernát6a8d1d92019-04-24 20:30:36 +0200279 }
280}
Václav Kubernát9bf36852020-02-18 17:47:56 +0100281}
Václav Kubernát6a8d1d92019-04-24 20:30:36 +0200282
Václav Kubernát9bf36852020-02-18 17:47:56 +0100283yang::LeafDataTypes YangSchema::leafType(const schemaPath_& location, const ModuleNodePair& node) const
284{
285 return impl_leafType(getSchemaNode(location, node));
286}
287
288yang::LeafDataTypes YangSchema::leafType(const std::string& path) const
289{
290 return impl_leafType(getSchemaNode(path));
291}
Václav Kubernát6a8d1d92019-04-24 20:30:36 +0200292
Václav Kubernát6fcd0282020-02-21 16:33:08 +0100293std::optional<std::string> YangSchema::leafTypeName(const std::string& path) const
294{
295 libyang::Schema_Node_Leaf leaf(getSchemaNode(path));
296 return leaf.type()->der().get() ? std::optional{leaf.type()->der()->name()} : std::nullopt;
297}
298
Václav Kubernátad87ece2020-02-19 15:20:56 +0100299namespace {
300yang::LeafDataTypes impl_leafrefBaseType(const libyang::S_Schema_Node& node)
Václav Kubernát6a8d1d92019-04-24 20:30:36 +0200301{
302 using namespace std::string_literals;
Václav Kubernátad87ece2020-02-19 15:20:56 +0100303 libyang::Schema_Node_Leaf leaf(node);
Václav Kubernát6a8d1d92019-04-24 20:30:36 +0200304 try {
305 return lyTypeToLeafDataTypes(leaf.type()->info()->lref()->target()->type()->base());
Václav Kubernát90de9502019-11-20 17:19:44 +0100306 } catch (std::logic_error& ex) {
Václav Kubernátad87ece2020-02-19 15:20:56 +0100307 throw UnsupportedYangTypeException("the type of "s + node->name() + " is not supported: " + ex.what());
Václav Kubernát0d4db442018-07-18 17:18:43 +0200308 }
309}
Václav Kubernátad87ece2020-02-19 15:20:56 +0100310}
311
312yang::LeafDataTypes YangSchema::leafrefBaseType(const schemaPath_& location, const ModuleNodePair& node) const
313{
314 return impl_leafrefBaseType(getSchemaNode(location, node));
315}
316
317yang::LeafDataTypes YangSchema::leafrefBaseType(const std::string& path) const
318{
319 return impl_leafrefBaseType(getSchemaNode(path));
320}
Václav Kubernát0d4db442018-07-18 17:18:43 +0200321
Václav Kubernátbd5e3c22020-02-19 15:22:00 +0100322std::string YangSchema::leafrefPath(const std::string& leafrefPath) const
323{
324 using namespace std::string_literals;
325 libyang::Schema_Node_Leaf leaf(getSchemaNode(leafrefPath));
326 return leaf.type()->info()->lref()->target()->path(LYS_PATH_FIRST_PREFIX);
327}
Václav Kubernát0d4db442018-07-18 17:18:43 +0200328
329std::set<std::string> YangSchema::modules() const
330{
Jan Kundrát4030b772018-08-23 15:54:56 +0200331 const auto& modules = m_context->get_module_iter();
Václav Kubernát0d4db442018-07-18 17:18:43 +0200332
333 std::set<std::string> res;
Václav Kubernát90de9502019-11-20 17:19:44 +0100334 std::transform(modules.begin(), modules.end(), std::inserter(res, res.end()), [](const auto module) { return module->name(); });
Václav Kubernát0d4db442018-07-18 17:18:43 +0200335 return res;
336}
337
Václav Kubernát2eaceb82018-10-08 19:56:30 +0200338std::set<std::string> YangSchema::childNodes(const schemaPath_& path, const Recursion recursion) const
Václav Kubernát0d4db442018-07-18 17:18:43 +0200339{
340 using namespace std::string_view_literals;
341 std::set<std::string> res;
Václav Kubernáte7d4aea2018-09-11 18:15:48 +0200342 std::vector<libyang::S_Schema_Node> nodes;
343
Václav Kubernát0d4db442018-07-18 17:18:43 +0200344 if (path.m_nodes.empty()) {
Václav Kubernáte7d4aea2018-09-11 18:15:48 +0200345 nodes = m_context->data_instantiables(0);
Václav Kubernát0d4db442018-07-18 17:18:43 +0200346 } else {
Václav Kubernátefcac932020-01-10 15:26:32 +0100347 const auto pathString = pathToSchemaString(path, Prefixes::Always);
348 const auto node = getSchemaNode(pathString);
Václav Kubernát47a3f672019-11-08 15:42:43 +0100349 nodes = node->child_instantiables(0);
Václav Kubernát0d4db442018-07-18 17:18:43 +0200350 }
Václav Kubernáte7d4aea2018-09-11 18:15:48 +0200351
352 for (const auto node : nodes) {
353 if (node->module()->name() == "ietf-yang-library"sv)
Václav Kubernát89728d82018-09-13 16:28:28 +0200354 continue;
Václav Kubernátfa81c8c2020-02-13 17:22:46 +0100355 // FIXME: This is a temporary fix to filter out RPC nodes in
356 // tab-completion. The method will have to be changed/reworked when RPC
357 // support gets added.
358 if (node->nodetype() == LYS_RPC)
359 continue;
Václav Kubernáte7d4aea2018-09-11 18:15:48 +0200360 if (recursion == Recursion::Recursive) {
361 for (auto it : node->tree_dfs()) {
362 res.insert(it->path(LYS_PATH_FIRST_PREFIX));
363 }
364 } else {
Václav Kubernát4f77a252019-02-19 16:51:30 +0100365 std::string toInsert;
366 if (path.m_nodes.empty() || path.m_nodes.front().m_prefix.get().m_name != node->module()->name()) {
367 toInsert += node->module()->name();
368 toInsert += ":";
369 }
370 toInsert += node->name();
371 res.insert(toInsert);
Václav Kubernáte7d4aea2018-09-11 18:15:48 +0200372 }
373 }
374
Václav Kubernát0d4db442018-07-18 17:18:43 +0200375 return res;
376}
Václav Kubernáta6c5fff2018-09-07 15:16:25 +0200377
Václav Kubernát9456b5c2019-10-02 21:14:52 +0200378std::set<std::string> YangSchema::moduleNodes(const module_& module, const Recursion recursion) const
379{
380 std::set<std::string> res;
381 const auto yangModule = m_context->get_module(module.m_name.c_str());
382
383 std::vector<libyang::S_Schema_Node> nodes;
384
385 for (const auto node : yangModule->data_instantiables(0)) {
386 if (recursion == Recursion::Recursive) {
387 for (const auto it : node->tree_dfs()) {
388 res.insert(it->path(LYS_PATH_FIRST_PREFIX));
389 }
390 } else {
391 res.insert(module.m_name + ":" + node->name());
392 }
393 }
394
395 return res;
396}
397
Václav Kubernáta6c5fff2018-09-07 15:16:25 +0200398void YangSchema::loadModule(const std::string& moduleName)
399{
400 m_context->load_module(moduleName.c_str());
401}
402
Václav Kubernátbf9c6112019-11-04 16:03:35 +0100403void YangSchema::enableFeature(const std::string& moduleName, const std::string& featureName)
404{
405 m_context->get_module(moduleName.c_str())->feature_enable(featureName.c_str());
406}
407
Václav Kubernátb52dc252019-12-04 13:03:39 +0100408void YangSchema::registerModuleCallback(const std::function<std::string(const char*, const char*, const char*, const char*)>& clb)
Václav Kubernáta6c5fff2018-09-07 15:16:25 +0200409{
410 auto lambda = [clb](const char* mod_name, const char* mod_revision, const char* submod_name, const char* submod_revision) {
411 (void)submod_revision;
Václav Kubernátb52dc252019-12-04 13:03:39 +0100412 auto moduleSource = clb(mod_name, mod_revision, submod_name, submod_revision);
Václav Kubernáta6c5fff2018-09-07 15:16:25 +0200413 if (moduleSource.empty()) {
414 return libyang::Context::mod_missing_cb_return{LYS_IN_YANG, nullptr};
415 }
416 return libyang::Context::mod_missing_cb_return{LYS_IN_YANG, strdup(moduleSource.c_str())};
417 };
418
419 auto deleter = [](void* data) {
420 free(data);
421 };
422 m_context->add_missing_module_callback(lambda, deleter);
423}
Václav Kubernátc31bd602019-03-07 11:44:48 +0100424
425std::shared_ptr<libyang::Data_Node> YangSchema::dataNodeFromPath(const std::string& path, const std::optional<const std::string> value) const
426{
427 return std::make_shared<libyang::Data_Node>(m_context,
428 path.c_str(),
429 value ? value.value().c_str() : nullptr,
430 LYD_ANYDATA_CONSTSTRING,
Václav Kubernátab612e92019-11-26 19:51:31 +0100431 LYD_PATH_OPT_EDIT);
Václav Kubernátc31bd602019-03-07 11:44:48 +0100432}
433
434std::shared_ptr<libyang::Module> YangSchema::getYangModule(const std::string& name)
435{
436 return m_context->get_module(name.c_str(), nullptr, 0);
437}
Václav Kubernát34ee85a2020-02-18 17:12:12 +0100438
439namespace {
440yang::NodeTypes impl_nodeType(const libyang::S_Schema_Node& node)
441{
442 if (!node) {
443 throw InvalidNodeException();
444 }
445 switch (node->nodetype()) {
446 case LYS_CONTAINER:
447 return libyang::Schema_Node_Container{node}.presence() ? yang::NodeTypes::PresenceContainer : yang::NodeTypes::Container;
448 case LYS_LEAF:
449 return yang::NodeTypes::Leaf;
450 case LYS_LIST:
451 return yang::NodeTypes::List;
452 default:
Václav Kubernát2a141392020-02-18 17:12:32 +0100453 throw InvalidNodeException(); // FIXME: Implement all types.
Václav Kubernát34ee85a2020-02-18 17:12:12 +0100454 }
455}
456}
457
458yang::NodeTypes YangSchema::nodeType(const schemaPath_& location, const ModuleNodePair& node) const
459{
460 return impl_nodeType(getSchemaNode(location, node));
461}
462
463yang::NodeTypes YangSchema::nodeType(const std::string& path) const
464{
465 return impl_nodeType(getSchemaNode(path));
466}
Václav Kubernát1e09bd62020-02-17 15:13:38 +0100467
468std::optional<std::string> YangSchema::description(const std::string& path) const
469{
470 auto node = getSchemaNode(path.c_str());
471 return node->dsc() ? std::optional{node->dsc()} : std::nullopt;
472}
473
474std::optional<std::string> YangSchema::units(const std::string& path) const
475{
476 auto node = getSchemaNode(path.c_str());
477 if (node->nodetype() != LYS_LEAF) {
478 return std::nullopt;
479 }
480 libyang::Schema_Node_Leaf leaf{node};
481 auto units = leaf.units();
482
483 // A leaf can specify units as part of its definition.
484 if (units) {
485 return units;
486 }
487
488 // A typedef (or its parent typedefs) can specify units too. We'll use the first `units` we find.
489 for (auto parentTypedef = leaf.type()->der(); parentTypedef; parentTypedef = parentTypedef->type()->der()) {
490 units = parentTypedef->units();
491 if (units) {
492 return units;
493 }
494 }
495
496 return std::nullopt;
497}