Add the describe command
...which should, hopefully, show useful information about a particular
YANG node, such as the YANG-level description, the units, type info,
etc.
Change-Id: Id430ae58fe353124e5132fa5a69378e98932ebce
diff --git a/src/ast_commands.hpp b/src/ast_commands.hpp
index 7f3160a..397aeb8 100644
--- a/src/ast_commands.hpp
+++ b/src/ast_commands.hpp
@@ -144,8 +144,27 @@
boost::optional<boost::variant<boost::variant<dataPath_, schemaPath_>, module_>> m_path;
};
+struct describe_ : x3::position_tagged {
+ static constexpr auto name = "describe";
+ static constexpr auto shortHelp = "describe - Print information about YANG tree path.";
+ static constexpr auto longHelp = R"(
+ describe <path>
+
+ Show documentation of YANG tree paths. In the YANG model, each item may
+ have an optional `description` which often explains the function of that
+ node to the end user. This command takes the description from the YANG
+ model and shows it to the user along with additional data, such as the type
+ of the node, units of leaf values, etc.
+
+ Usage:
+ /> describe /module:node)";
+ bool operator==(const describe_& b) const;
+
+ boost::variant<schemaPath_, dataPath_> m_path;
+};
+
struct help_;
-using CommandTypes = boost::mpl::vector<discard_, ls_, cd_, create_, delete_, set_, commit_, get_, help_>;
+using CommandTypes = boost::mpl::vector<discard_, ls_, cd_, create_, delete_, set_, commit_, get_, describe_, help_>;
struct help_ : x3::position_tagged {
static constexpr auto name = "help";
static constexpr auto shortHelp = "help - Print help for commands.";
@@ -186,6 +205,7 @@
BOOST_FUSION_ADAPT_STRUCT(binary_, m_value)
BOOST_FUSION_ADAPT_STRUCT(identityRef_, m_prefix, m_value)
BOOST_FUSION_ADAPT_STRUCT(commit_)
+BOOST_FUSION_ADAPT_STRUCT(describe_, m_path)
BOOST_FUSION_ADAPT_STRUCT(help_, m_cmd)
BOOST_FUSION_ADAPT_STRUCT(discard_)
BOOST_FUSION_ADAPT_STRUCT(get_, m_path)
diff --git a/src/ast_handlers.hpp b/src/ast_handlers.hpp
index 456ef11..a8dd4ae 100644
--- a/src/ast_handlers.hpp
+++ b/src/ast_handlers.hpp
@@ -497,6 +497,8 @@
struct commit_class;
+struct describe_class;
+
struct help_class;
struct get_class;
diff --git a/src/grammars.hpp b/src/grammars.hpp
index dd13c6a..3159fe0 100644
--- a/src/grammars.hpp
+++ b/src/grammars.hpp
@@ -65,6 +65,7 @@
x3::rule<create_class, create_> const create = "create";
x3::rule<delete_class, delete_> const delete_rule = "delete_rule";
x3::rule<commit_class, commit_> const commit = "commit";
+x3::rule<describe_class, describe_> const describe = "describe";
x3::rule<help_class, help_> const help = "help";
x3::rule<command_class, command_> const command = "command";
@@ -332,11 +333,14 @@
auto const help_def =
help_::name > createCommandSuggestions >> -command_names;
+auto const describe_def =
+ describe_::name >> space_separator > (dataPathListEnd | dataPath | schemaPath);
+
auto const createCommandSuggestions_def =
x3::eps;
auto const command_def =
- createCommandSuggestions >> x3::expect[cd | create | delete_rule | set | commit | get | ls | discard | help];
+ createCommandSuggestions >> x3::expect[cd | create | delete_rule | set | commit | get | ls | discard | describe | help];
#if __clang__
#pragma GCC diagnostic pop
@@ -393,6 +397,7 @@
BOOST_SPIRIT_DEFINE(cd)
BOOST_SPIRIT_DEFINE(create)
BOOST_SPIRIT_DEFINE(delete_rule)
+BOOST_SPIRIT_DEFINE(describe)
BOOST_SPIRIT_DEFINE(help)
BOOST_SPIRIT_DEFINE(command)
BOOST_SPIRIT_DEFINE(createPathSuggestions)
diff --git a/src/interpreter.cpp b/src/interpreter.cpp
index f112b52..5b13337 100644
--- a/src/interpreter.cpp
+++ b/src/interpreter.cpp
@@ -8,6 +8,7 @@
#include <boost/mpl/for_each.hpp>
#include <iostream>
+#include <sstream>
#include "datastore_access.hpp"
#include "interpreter.hpp"
#include "utils.hpp"
@@ -79,6 +80,60 @@
std::cout << it << std::endl;
}
+std::string Interpreter::buildTypeInfo(const std::string& path) const
+{
+ std::ostringstream ss;
+ switch (m_datastore.schema()->nodeType(path)) {
+ case yang::NodeTypes::Container:
+ ss << "container";
+ break;
+ case yang::NodeTypes::PresenceContainer:
+ ss << "presence container";
+ break;
+ case yang::NodeTypes::Leaf:
+ {
+ auto leafType = m_datastore.schema()->leafType(path);
+ auto typedefName = m_datastore.schema()->leafTypeName(path);
+ std::string baseTypeStr;
+ if (leafType == yang::LeafDataTypes::LeafRef) {
+ ss << "-> ";
+ ss << m_datastore.schema()->leafrefPath(path) << " ";
+ baseTypeStr = leafDataTypeToString(m_datastore.schema()->leafrefBaseType(path));
+ } else {
+ baseTypeStr = leafDataTypeToString(leafType);
+ }
+
+ if (typedefName) {
+ ss << *typedefName << " (" << baseTypeStr << ")";
+ } else {
+ ss << baseTypeStr;
+ }
+
+ if (auto units = m_datastore.schema()->units(path)) {
+ ss << " [" + *units + "]";
+ }
+
+ if (m_datastore.schema()->leafIsKey(path)) {
+ ss << " (key)";
+ }
+ break;
+ }
+ case yang::NodeTypes::List:
+ ss << "list";
+ break;
+ }
+ return ss.str();
+}
+
+void Interpreter::operator()(const describe_& describe) const
+{
+ auto path = absolutePathFromCommand(describe);
+ std::cout << path << ": " << buildTypeInfo(path) << std::endl;
+ if (auto description = m_datastore.schema()->description(path)) {
+ std::cout << std::endl << *description << std::endl;
+ }
+}
+
struct commandLongHelpVisitor : boost::static_visitor<const char*> {
template <typename T>
auto constexpr operator()(boost::type<T>) const
@@ -156,6 +211,15 @@
}
}
+std::string Interpreter::absolutePathFromCommand(const describe_& describe) const
+{
+ auto pathStr = boost::apply_visitor(pathToStringVisitor(), describe.m_path);
+ if (boost::apply_visitor(getPathScopeVisitor(), describe.m_path) == Scope::Absolute)
+ return pathStr;
+ else
+ return joinPaths(m_parser.currentNode(), pathStr);
+}
+
Interpreter::Interpreter(Parser& parser, DatastoreAccess& datastore)
: m_parser(parser)
, m_datastore(datastore)
diff --git a/src/interpreter.hpp b/src/interpreter.hpp
index 7af5983..a05183f 100644
--- a/src/interpreter.hpp
+++ b/src/interpreter.hpp
@@ -22,6 +22,7 @@
void operator()(const create_&) const;
void operator()(const delete_&) const;
void operator()(const ls_&) const;
+ void operator()(const describe_&) const;
void operator()(const discard_&) const;
void operator()(const help_&) const;
@@ -29,6 +30,8 @@
template <typename T>
std::string absolutePathFromCommand(const T& command) const;
std::string absolutePathFromCommand(const get_& command) const;
+ std::string absolutePathFromCommand(const describe_& describe) const;
+ std::string buildTypeInfo(const std::string& path) const;
Parser& m_parser;
DatastoreAccess& m_datastore;
diff --git a/tests/command_completion.cpp b/tests/command_completion.cpp
index 1d44646..eb4949a 100644
--- a/tests/command_completion.cpp
+++ b/tests/command_completion.cpp
@@ -22,14 +22,14 @@
SECTION("")
{
input = "";
- expectedCompletions = {"cd", "create", "delete", "set", "commit", "get", "ls", "discard", "help"};
+ expectedCompletions = {"cd", "create", "delete", "set", "commit", "get", "ls", "discard", "help", "describe"};
expectedContextLength = 0;
}
SECTION(" ")
{
input = " ";
- expectedCompletions = {"cd", "create", "delete", "set", "commit", "get", "ls", "discard", "help"};
+ expectedCompletions = {"cd", "create", "delete", "set", "commit", "get", "ls", "discard", "help", "describe"};
expectedContextLength = 0;
}
@@ -43,7 +43,7 @@
SECTION("d")
{
input = "d";
- expectedCompletions = {"delete", "discard"};
+ expectedCompletions = {"delete", "discard", "describe"};
expectedContextLength = 1;
}
diff --git a/tests/example-schema.yang b/tests/example-schema.yang
index cbd5d8d..f876779 100644
--- a/tests/example-schema.yang
+++ b/tests/example-schema.yang
@@ -57,6 +57,7 @@
}
leaf leafDecimal {
+ units "nm";
type decimal64 {
fraction-digits 9;
}