Allow listing all module nodes with moduleName:*

Change-Id: I1c9d8ace936641db06f38c292655b3c5c5557385
diff --git a/src/ast_commands.hpp b/src/ast_commands.hpp
index 5f8f052..c5ffb61 100644
--- a/src/ast_commands.hpp
+++ b/src/ast_commands.hpp
@@ -50,7 +50,7 @@
         /> ls /module:node)";
     bool operator==(const ls_& b) const;
     std::vector<LsOption> m_options;
-    boost::optional<boost::variant<dataPath_, schemaPath_>> m_path;
+    boost::optional<boost::variant<boost::variant<dataPath_, schemaPath_>, module_>> m_path;
 };
 
 struct cd_ : x3::position_tagged {
@@ -141,7 +141,7 @@
         /> get
         /> get /module:path)";
     bool operator==(const get_& b) const;
-    boost::optional<boost::variant<dataPath_, schemaPath_>> m_path;
+    boost::optional<boost::variant<boost::variant<dataPath_, schemaPath_>, module_>> m_path;
 };
 
 struct help_;
diff --git a/src/grammars.hpp b/src/grammars.hpp
index 2c511ad..4fd7b15 100644
--- a/src/grammars.hpp
+++ b/src/grammars.hpp
@@ -305,7 +305,7 @@
     x3::eps;
 
 auto const ls_def =
-    ls_::name >> *(space_separator >> ls_options) >> -(space_separator >> (dataPathListEnd | dataPath | schemaPath));
+    ls_::name >> *(space_separator >> ls_options) >> -(space_separator >> ((dataPathListEnd | dataPath | schemaPath) | (module >> "*")));
 
 auto const cd_def =
     cd_::name >> space_separator > dataPath;
@@ -317,7 +317,7 @@
     delete_::name >> space_separator > (presenceContainerPath | listInstancePath);
 
 auto const get_def =
-    get_::name >> -(space_separator >> (dataPathListEnd | dataPath));
+    get_::name >> -(space_separator >> ((dataPathListEnd | dataPath) | (module >> "*")));
 
 auto const set_def =
     set_::name >> space_separator > leafPath > space_separator > leaf_data;
diff --git a/src/interpreter.cpp b/src/interpreter.cpp
index b469149..3630c25 100644
--- a/src/interpreter.cpp
+++ b/src/interpreter.cpp
@@ -135,18 +135,25 @@
 
 std::string Interpreter::absolutePathFromCommand(const get_& get) const
 {
+    using namespace std::string_literals;
     if (!get.m_path) {
         return m_parser.currentNode();
     }
 
     const auto path = *get.m_path;
-    std::string pathString = boost::apply_visitor(pathToStringVisitor(), path);
-    auto pathScope{boost::apply_visitor(getPathScopeVisitor(), path)};
+    if (path.type() == typeid(module_)) {
+        return "/"s + boost::get<module_>(path).m_name + ":*";
 
-    if (pathScope == Scope::Absolute) {
-        return "/" + pathString;
     } else {
-        return joinPaths(m_parser.currentNode(), pathString);
+        auto actualPath = boost::get<boost::variant<dataPath_, schemaPath_>>(path);
+        std::string pathString = boost::apply_visitor(pathToStringVisitor(), actualPath);
+        auto pathScope{boost::apply_visitor(getPathScopeVisitor(), actualPath)};
+
+        if (pathScope == Scope::Absolute) {
+            return "/" + pathString;
+        } else {
+            return joinPaths(m_parser.currentNode(), pathString);
+        }
     }
 }
 
diff --git a/src/netconf_access.cpp b/src/netconf_access.cpp
index 13fec88..c1bd5dc 100644
--- a/src/netconf_access.cpp
+++ b/src/netconf_access.cpp
@@ -64,15 +64,13 @@
         }
     };
 
-    if (path == "/") {
-        for (auto it : m_session->getConfig(NC_DATASTORE_RUNNING)->tree_for()) {
+    auto config = m_session->getConfig(NC_DATASTORE_RUNNING, (path != "/") ? std::optional{path} : std::nullopt);
+
+    if (config) {
+        for (auto it : config->tree_for()) {
             fillMap(it->tree_dfs());
         }
-    } else {
-        auto data = m_session->getConfig(NC_DATASTORE_RUNNING, path);
-        fillMap(data->tree_dfs());
     }
-
     return res;
 }
 
diff --git a/src/parser.cpp b/src/parser.cpp
index 26e186a..370ba1b 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -95,11 +95,15 @@
 };
 
 
-std::set<std::string> Parser::availableNodes(const boost::optional<boost::variant<dataPath_, schemaPath_>>& path, const Recursion& option) const
+std::set<std::string> Parser::availableNodes(const boost::optional<boost::variant<boost::variant<dataPath_, schemaPath_>, module_>>& path, const Recursion& option) const
 {
     auto pathArg = dataPathToSchemaPath(m_curDir);
     if (path) {
-        auto schemaPath = boost::apply_visitor(getSchemaPathVisitor(), *path);
+        if (path->type() == typeid(module_)) {
+            return m_schema->moduleNodes(boost::get<module_>(*path), option);
+        }
+
+        auto schemaPath = boost::apply_visitor(getSchemaPathVisitor(), boost::get<boost::variant<dataPath_, schemaPath_>>(*path));
         if (schemaPath.m_scope == Scope::Absolute) {
             pathArg = schemaPath;
         } else {
diff --git a/src/parser.hpp b/src/parser.hpp
index 29ce3f1..3c009e6 100644
--- a/src/parser.hpp
+++ b/src/parser.hpp
@@ -30,7 +30,7 @@
     command_ parseCommand(const std::string& line, std::ostream& errorStream);
     void changeNode(const dataPath_& name);
     std::string currentNode() const;
-    std::set<std::string> availableNodes(const boost::optional<boost::variant<dataPath_, schemaPath_>>& path, const Recursion& option) const;
+    std::set<std::string> availableNodes(const boost::optional<boost::variant<boost::variant<dataPath_, schemaPath_>, module_>>& path, const Recursion& option) const;
     std::set<std::string> completeCommand(const std::string& line, std::ostream& errorStream) const;
 
 private:
diff --git a/src/schema.hpp b/src/schema.hpp
index e0d30af..b5e17a2 100644
--- a/src/schema.hpp
+++ b/src/schema.hpp
@@ -79,4 +79,5 @@
     virtual const std::set<std::string> validIdentities(const schemaPath_& location, const ModuleNodePair& node, const Prefixes prefixes) const = 0;
     virtual const std::set<std::string> enumValues(const schemaPath_& location, const ModuleNodePair& node) const = 0;
     virtual std::set<std::string> childNodes(const schemaPath_& path, const Recursion recursion) const = 0;
+    virtual std::set<std::string> moduleNodes(const module_& module, const Recursion recursion) const = 0;
 };
diff --git a/src/static_schema.cpp b/src/static_schema.cpp
index 3c3fced..a3f59aa 100644
--- a/src/static_schema.cpp
+++ b/src/static_schema.cpp
@@ -6,6 +6,7 @@
  *
 */
 
+#include <boost/algorithm/string/predicate.hpp>
 #include "static_schema.hpp"
 #include "utils.hpp"
 
@@ -254,3 +255,18 @@
                 [] (auto it) { return it.first; });
     return res;
 }
+
+// We do not test StaticSchema, so we don't need to implement recursive moduleNodes
+// for this class.
+std::set<std::string> StaticSchema::moduleNodes(const module_& module, const Recursion) const
+{
+    std::set<std::string> res;
+    auto topLevelNodes = m_nodes.at("");
+    auto modulePlusColon = module.m_name + ":";
+    for (const auto& it : topLevelNodes) {
+        if (boost::algorithm::starts_with(it.first, modulePlusColon)) {
+            res.insert(it.first);
+        }
+    }
+    return res;
+}
diff --git a/src/static_schema.hpp b/src/static_schema.hpp
index 559be6c..09841ac 100644
--- a/src/static_schema.hpp
+++ b/src/static_schema.hpp
@@ -63,6 +63,7 @@
     const std::set<std::string> enumValues(const schemaPath_& location, const ModuleNodePair& node) const override;
     const std::set<std::string> validIdentities(const schemaPath_& location, const ModuleNodePair& node, const Prefixes prefixes) const override;
     std::set<std::string> childNodes(const schemaPath_& path, const Recursion) const override;
+    std::set<std::string> moduleNodes(const module_& module, const Recursion recursion) const override;
 
     void addContainer(const std::string& location, const std::string& name, yang::ContainerTraits isPresence = yang::ContainerTraits::None);
     void addLeaf(const std::string& location, const std::string& name, const yang::LeafDataTypes& type);
diff --git a/src/yang_schema.cpp b/src/yang_schema.cpp
index 629aa2b..c9e3d3c 100644
--- a/src/yang_schema.cpp
+++ b/src/yang_schema.cpp
@@ -346,6 +346,26 @@
     return res;
 }
 
+std::set<std::string> YangSchema::moduleNodes(const module_& module, const Recursion recursion) const
+{
+    std::set<std::string> res;
+    const auto yangModule = m_context->get_module(module.m_name.c_str());
+
+    std::vector<libyang::S_Schema_Node> nodes;
+
+    for (const auto node : yangModule->data_instantiables(0)) {
+        if (recursion == Recursion::Recursive) {
+            for (const auto it : node->tree_dfs()) {
+                res.insert(it->path(LYS_PATH_FIRST_PREFIX));
+            }
+        } else {
+            res.insert(module.m_name + ":" + node->name());
+        }
+    }
+
+    return res;
+}
+
 void YangSchema::loadModule(const std::string& moduleName)
 {
     m_context->load_module(moduleName.c_str());
diff --git a/src/yang_schema.hpp b/src/yang_schema.hpp
index fb4b922..e4de5de 100644
--- a/src/yang_schema.hpp
+++ b/src/yang_schema.hpp
@@ -44,6 +44,7 @@
     const std::set<std::string> validIdentities(const schemaPath_& location, const ModuleNodePair& node, const Prefixes prefixes) const override;
     const std::set<std::string> enumValues(const schemaPath_& location, const ModuleNodePair& node) const override;
     std::set<std::string> childNodes(const schemaPath_& path, const Recursion recursion) const override;
+    std::set<std::string> moduleNodes(const module_& module, const Recursion recursion) const override;
 
     void registerModuleCallback(const std::function<std::string(const char*, const char*, const char*)>& clb);