Add Schema::nodeType method

Change-Id: Id17e00ebef4820abae3b66d2e262be4b16c7a672
diff --git a/src/schema.hpp b/src/schema.hpp
index 8b7031a..6970fe9 100644
--- a/src/schema.hpp
+++ b/src/schema.hpp
@@ -35,6 +35,13 @@
     IdentityRef,
     LeafRef,
 };
+
+enum class NodeTypes {
+    Container,
+    PresenceContainer,
+    List,
+    Leaf
+};
 }
 
 enum class Recursion {
@@ -43,10 +50,7 @@
 };
 
 
-class InvalidNodeException : public std::invalid_argument {
-public:
-    using std::invalid_argument::invalid_argument;
-    ~InvalidNodeException() override;
+class InvalidNodeException {
 };
 
 /*! \class Schema
@@ -61,6 +65,8 @@
 
     virtual bool isContainer(const schemaPath_& location, const ModuleNodePair& node) const = 0;
     virtual bool isLeaf(const schemaPath_& location, const ModuleNodePair& node) const = 0;
+    virtual yang::NodeTypes nodeType(const std::string& path) const = 0;
+    virtual yang::NodeTypes nodeType(const schemaPath_& location, const ModuleNodePair& node) const = 0;
     virtual bool isModule(const std::string& name) const = 0;
     virtual bool isList(const schemaPath_& location, const ModuleNodePair& node) const = 0;
     virtual bool isPresenceContainer(const schemaPath_& location, const ModuleNodePair& node) const = 0;
diff --git a/src/static_schema.cpp b/src/static_schema.cpp
index 47e0884..aae6b84 100644
--- a/src/static_schema.cpp
+++ b/src/static_schema.cpp
@@ -10,8 +10,6 @@
 #include "static_schema.hpp"
 #include "utils.hpp"
 
-InvalidNodeException::~InvalidNodeException() = default;
-
 StaticSchema::StaticSchema()
 {
     m_nodes.emplace("/", std::unordered_map<std::string, NodeType>());
@@ -284,3 +282,37 @@
     }
     return res;
 }
+
+yang::NodeTypes StaticSchema::nodeType(const schemaPath_& location, const ModuleNodePair& node) const
+{
+    std::string locationString = pathToSchemaString(location, Prefixes::Always);
+    auto fullName = fullNodeName(location, node);
+    try {
+        auto targetNode = children(locationString).at(fullName);
+
+        if (targetNode.type() == typeid(yang::container)) {
+            if (boost::get<yang::container>(targetNode).m_presence == yang::ContainerTraits::Presence) {
+                return yang::NodeTypes::PresenceContainer;
+            }
+            return yang::NodeTypes::Container;
+        }
+
+        if (targetNode.type() == typeid(yang::list)) {
+            return yang::NodeTypes::List;
+        }
+
+        if (targetNode.type() == typeid(yang::leaf)) {
+            return yang::NodeTypes::Leaf;
+        }
+
+        throw std::runtime_error{"YangSchema::nodeType: unsupported type"};
+
+    } catch (std::out_of_range&) {
+        throw InvalidNodeException();
+    }
+}
+
+yang::NodeTypes StaticSchema::nodeType([[maybe_unused]] const std::string& path) const
+{
+    throw std::runtime_error{"Internal error: StaticSchema::nodeType(std::string) not implemented. The tests should not have called this overload."};
+}
diff --git a/src/static_schema.hpp b/src/static_schema.hpp
index 742c53d..8ff3405 100644
--- a/src/static_schema.hpp
+++ b/src/static_schema.hpp
@@ -49,6 +49,8 @@
     StaticSchema();
 
     bool isContainer(const schemaPath_& location, const ModuleNodePair& node) const override;
+    yang::NodeTypes nodeType(const std::string& path) const override;
+    yang::NodeTypes nodeType(const schemaPath_& location, const ModuleNodePair& node) const override;
     bool isModule(const std::string& name) const override;
     bool isLeaf(const schemaPath_& location, const ModuleNodePair& node) const override;
     bool isList(const schemaPath_& location, const ModuleNodePair& node) const override;
diff --git a/src/yang_schema.cpp b/src/yang_schema.cpp
index dddebf5..3470865 100644
--- a/src/yang_schema.cpp
+++ b/src/yang_schema.cpp
@@ -412,3 +412,32 @@
 {
     return m_context->get_module(name.c_str(), nullptr, 0);
 }
+
+namespace {
+yang::NodeTypes impl_nodeType(const libyang::S_Schema_Node& node)
+{
+    if (!node) {
+        throw InvalidNodeException();
+    }
+    switch (node->nodetype()) {
+    case LYS_CONTAINER:
+        return libyang::Schema_Node_Container{node}.presence() ? yang::NodeTypes::PresenceContainer : yang::NodeTypes::Container;
+    case LYS_LEAF:
+        return yang::NodeTypes::Leaf;
+    case LYS_LIST:
+        return yang::NodeTypes::List;
+    default:
+        throw std::runtime_error{"YangSchema::nodeType: unsupported type"};
+    }
+}
+}
+
+yang::NodeTypes YangSchema::nodeType(const schemaPath_& location, const ModuleNodePair& node) const
+{
+    return impl_nodeType(getSchemaNode(location, node));
+}
+
+yang::NodeTypes YangSchema::nodeType(const std::string& path) const
+{
+    return impl_nodeType(getSchemaNode(path));
+}
diff --git a/src/yang_schema.hpp b/src/yang_schema.hpp
index 1ff5144..dd88145 100644
--- a/src/yang_schema.hpp
+++ b/src/yang_schema.hpp
@@ -32,6 +32,8 @@
 
     bool isContainer(const schemaPath_& location, const ModuleNodePair& node) const override;
     bool isLeaf(const schemaPath_& location, const ModuleNodePair& node) const override;
+    yang::NodeTypes nodeType(const std::string& path) const override;
+    yang::NodeTypes nodeType(const schemaPath_& location, const ModuleNodePair& node) const override;
     bool isModule(const std::string& name) const override;
     bool isList(const schemaPath_& location, const ModuleNodePair& node) const override;
     bool isPresenceContainer(const schemaPath_& location, const ModuleNodePair& node) const override;
diff --git a/tests/yang.cpp b/tests/yang.cpp
index 162a194..017107c 100644
--- a/tests/yang.cpp
+++ b/tests/yang.cpp
@@ -749,6 +749,37 @@
 
             REQUIRE(ys.childNodes(path, Recursion::NonRecursive) == set);
         }
+        SECTION("nodeType")
+        {
+            yang::NodeTypes expected;
+            SECTION("leafInt32")
+            {
+                path.m_nodes.push_back(schemaNode_(module_{"example-schema"}, leaf_("leafInt32")));
+                expected = yang::NodeTypes::Leaf;
+            }
+
+            SECTION("a")
+            {
+                path.m_nodes.push_back(schemaNode_(module_{"example-schema"}, container_("a")));
+                expected = yang::NodeTypes::Container;
+            }
+
+            SECTION("a/a2/a3")
+            {
+                path.m_nodes.push_back(schemaNode_(module_{"example-schema"}, container_("a")));
+                path.m_nodes.push_back(schemaNode_(container_("a2")));
+                path.m_nodes.push_back(schemaNode_(container_("a3")));
+                expected = yang::NodeTypes::PresenceContainer;
+            }
+
+            SECTION("_list")
+            {
+                path.m_nodes.push_back(schemaNode_(module_{"example-schema"}, list_("_list")));
+                expected = yang::NodeTypes::List;
+            }
+
+            REQUIRE(ys.nodeType(pathToSchemaString(path, Prefixes::WhenNeeded)) == expected);
+        }
     }
 
     SECTION("negative")