Add support for union leafs

Change-Id: Ifc1a53eed2c059f6fe5d75544ecaa3e63028f78f
diff --git a/src/leaf_data.hpp b/src/leaf_data.hpp
index 7139fc4..1c4176e 100644
--- a/src/leaf_data.hpp
+++ b/src/leaf_data.hpp
@@ -173,6 +173,12 @@
     {
         return std::visit(*this, *leafRef.m_targetType);
     }
+    bool operator()(const yang::Union& unionInfo) const
+    {
+        return std::any_of(unionInfo.m_unionTypes.begin(), unionInfo.m_unionTypes.end(), [this](const auto& type) {
+            return std::visit(*this, type);
+        });
+    }
 };
 
 struct LeafData : x3::parser<LeafData> {
diff --git a/src/leaf_data_type.cpp b/src/leaf_data_type.cpp
index 815d0aa..8048efa 100644
--- a/src/leaf_data_type.cpp
+++ b/src/leaf_data_type.cpp
@@ -39,6 +39,10 @@
 {
     return this->m_targetXPath == other.m_targetXPath && *this->m_targetType == *other.m_targetType;
 }
+bool Union::operator==(const Union& other) const
+{
+    return this->m_unionTypes == other.m_unionTypes;
+}
 bool String::operator==(const String&) const
 {
     return true;
diff --git a/src/leaf_data_type.hpp b/src/leaf_data_type.hpp
index 66f4522..a86691b 100644
--- a/src/leaf_data_type.hpp
+++ b/src/leaf_data_type.hpp
@@ -10,6 +10,7 @@
 #include <set>
 #include <string>
 #include <variant>
+#include <vector>
 
 struct enum_;
 struct identityRef_;
@@ -62,6 +63,7 @@
     std::set<identityRef_> m_allowedValues;
 };
 struct LeafRef;
+struct Union;
 using LeafDataType = std::variant<
     yang::String,
     yang::Decimal,
@@ -77,7 +79,8 @@
     yang::Enum,
     yang::Binary,
     yang::IdentityRef,
-    yang::LeafRef
+    yang::LeafRef,
+    yang::Union
 >;
 struct LeafRef {
     LeafRef(const LeafRef& src);
@@ -86,4 +89,9 @@
     std::string m_targetXPath;
     std::unique_ptr<LeafDataType> m_targetType;
 };
+
+struct Union {
+    bool operator==(const Union& other) const;
+    std::vector<LeafDataType> m_unionTypes;
+};
 }
diff --git a/src/utils.cpp b/src/utils.cpp
index 719f28b..31854b2 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -5,6 +5,7 @@
  * Written by Václav Kubernát <kubervac@fit.cvut.cz>
  *
 */
+#include <experimental/iterator>
 #include <sstream>
 #include "completion.hpp"
 #include "utils.hpp"
@@ -113,6 +114,14 @@
     {
         return "a leafref";
     }
+    std::string operator()(const yang::Union& type)
+    {
+        std::ostringstream ss;
+        std::transform(type.m_unionTypes.begin(), type.m_unionTypes.end(), std::experimental::make_ostream_joiner(ss, ", "), [this](const auto& unionType) {
+            return std::visit(*this, unionType);
+        });
+        return ss.str();
+    }
 };
 
 std::string leafDataTypeToString(const yang::LeafDataType& type)
diff --git a/src/yang_schema.cpp b/src/yang_schema.cpp
index 0f1a3fd..a453b50 100644
--- a/src/yang_schema.cpp
+++ b/src/yang_schema.cpp
@@ -154,9 +154,9 @@
 }
 
 namespace {
-std::set<enum_> enumValues(const libyang::S_Schema_Node_Leaf& leaf)
+std::set<enum_> enumValues(const libyang::S_Type& typeArg)
 {
-    auto type = leaf->type();
+    auto type = typeArg;
     auto enm = type->info()->enums()->enm();
     // The enum can be a derived type and enm() only returns values,
     // if that specific typedef changed the possible values. So we go
@@ -177,13 +177,13 @@
     return enumSet;
 }
 
-std::set<identityRef_> validIdentities(const libyang::S_Schema_Node_Leaf& leaf)
+std::set<identityRef_> validIdentities(const libyang::S_Type& type)
 {
     std::set<identityRef_> identSet;
 
     // auto topLevelModule = leaf->module();
 
-    auto info = leaf->type()->info();
+    auto info = type->info();
     for (auto base : info->ident()->ref()) { // Iterate over all bases
         identSet.emplace(base->module()->name(), base->name());
         // Iterate over derived identities (this is recursive!)
@@ -195,9 +195,9 @@
     return identSet;
 }
 
-std::string leafrefPath(const libyang::S_Schema_Node_Leaf& leaf)
+std::string leafrefPath(const libyang::S_Type& type)
 {
-    return leaf->type()->info()->lref()->target()->path(LYS_PATH_FIRST_PREFIX);
+    return type->info()->lref()->target()->path(LYS_PATH_FIRST_PREFIX);
 }
 }
 
@@ -205,42 +205,53 @@
 {
     using namespace std::string_literals;
     auto leaf = std::make_shared<libyang::Schema_Node_Leaf>(node);
-    auto baseType{leaf->type()->base()};
-    switch (baseType) {
-    case LY_TYPE_STRING:
-        return yang::String{};
-    case LY_TYPE_DEC64:
-        return yang::Decimal{};
-    case LY_TYPE_BOOL:
-        return yang::Bool{};
-    case LY_TYPE_INT8:
-        return yang::Int8{};
-    case LY_TYPE_INT16:
-        return yang::Int16{};
-    case LY_TYPE_INT32:
-        return yang::Int32{};
-    case LY_TYPE_INT64:
-        return yang::Int64{};
-    case LY_TYPE_UINT8:
-        return yang::Uint8{};
-    case LY_TYPE_UINT16:
-        return yang::Uint16{};
-    case LY_TYPE_UINT32:
-        return yang::Uint32{};
-    case LY_TYPE_UINT64:
-        return yang::Uint64{};
-    case LY_TYPE_BINARY:
-        return yang::Binary{};
-    case LY_TYPE_ENUM:
-        return yang::Enum{enumValues(leaf)};
-    case LY_TYPE_IDENT:
-        return yang::IdentityRef{validIdentities(leaf)};
-    case LY_TYPE_LEAFREF:
-        return yang::LeafRef{::leafrefPath(leaf), std::make_unique<yang::LeafDataType>(leafType(::leafrefPath(leaf)))};
-    default:
-        using namespace std::string_literals;
-        throw UnsupportedYangTypeException("the type of "s + node->name() + " is not supported: " + std::to_string(baseType));
-    }
+    std::function<yang::LeafDataType(std::shared_ptr<libyang::Type>)> resolveType;
+    resolveType = [this, &resolveType, leaf] (auto type) -> yang::LeafDataType {
+        switch (type->base()) {
+        case LY_TYPE_STRING:
+            return yang::String{};
+        case LY_TYPE_DEC64:
+            return yang::Decimal{};
+        case LY_TYPE_BOOL:
+            return yang::Bool{};
+        case LY_TYPE_INT8:
+            return yang::Int8{};
+        case LY_TYPE_INT16:
+            return yang::Int16{};
+        case LY_TYPE_INT32:
+            return yang::Int32{};
+        case LY_TYPE_INT64:
+            return yang::Int64{};
+        case LY_TYPE_UINT8:
+            return yang::Uint8{};
+        case LY_TYPE_UINT16:
+            return yang::Uint16{};
+        case LY_TYPE_UINT32:
+            return yang::Uint32{};
+        case LY_TYPE_UINT64:
+            return yang::Uint64{};
+        case LY_TYPE_BINARY:
+            return yang::Binary{};
+        case LY_TYPE_ENUM:
+            return yang::Enum{enumValues(type)};
+        case LY_TYPE_IDENT:
+            return yang::IdentityRef{validIdentities(type)};
+        case LY_TYPE_LEAFREF:
+            return yang::LeafRef{::leafrefPath(type), std::make_unique<yang::LeafDataType>(leafType(::leafrefPath(type)))};
+        case LY_TYPE_UNION:
+            {
+                auto res = yang::Union{};
+                for (auto unionType : type->info()->uni()->types()) {
+                    res.m_unionTypes.push_back(resolveType(unionType));
+                }
+                return res;
+            }
+        default:
+            using namespace std::string_literals;
+            throw UnsupportedYangTypeException("the type of "s + leaf->name() + " is not supported: " + std::to_string(leaf->type()->base()));
+        }
+        };
+    return resolveType(leaf->type());
 }
 
 yang::LeafDataType YangSchema::leafType(const schemaPath_& location, const ModuleNodePair& node) const