Merge changes from topic "ci-coverage"

* changes:
  CI: compare code coverage changes
  CI: start building with coverage profiling
  CI: only build docs once
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 519e2c3..0aec7e1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -75,6 +75,11 @@
     )
 target_link_libraries(path PUBLIC ast_values Boost::boost)
 
+add_library(leaf_data_type STATIC
+    src/leaf_data_type.cpp
+    )
+target_link_libraries(leaf_data_type ast_values)
+
 add_library(utils STATIC
     src/utils.cpp
     )
@@ -84,7 +89,7 @@
     src/static_schema.cpp
     src/schema.cpp
     )
-target_link_libraries(schemas PUBLIC path Boost::boost)
+target_link_libraries(schemas PUBLIC path leaf_data_type Boost::boost)
 
 add_library(datastoreaccess STATIC
     src/datastore_access.cpp
@@ -125,7 +130,6 @@
     src/interpreter.cpp
     src/ast_handlers.cpp
     src/completion.cpp
-    src/leaf_data.cpp
     )
 target_link_libraries(parser schemas utils ast_values)
 
@@ -274,12 +278,14 @@
     cli_test(ls)
     cli_test(presence_containers)
     cli_test(leaf_editing)
+    target_link_libraries(test_leaf_editing leaf_data_type)
     cli_test(yang)
     target_link_libraries(test_yang yangschema)
     cli_test(utils)
     cli_test(path_completion)
     cli_test(command_completion)
     cli_test(enum_completion)
+    target_link_libraries(test_enum_completion leaf_data_type)
     cli_test(list_manipulation)
     cli_test(parser_methods)
     cli_test(path_utils)
diff --git a/src/interpreter.cpp b/src/interpreter.cpp
index 5b13337..ca227be 100644
--- a/src/interpreter.cpp
+++ b/src/interpreter.cpp
@@ -95,10 +95,10 @@
         auto leafType = m_datastore.schema()->leafType(path);
         auto typedefName = m_datastore.schema()->leafTypeName(path);
         std::string baseTypeStr;
-        if (leafType == yang::LeafDataTypes::LeafRef) {
+        if (std::holds_alternative<yang::LeafRef>(leafType)) {
             ss << "-> ";
             ss << m_datastore.schema()->leafrefPath(path) << " ";
-            baseTypeStr = leafDataTypeToString(m_datastore.schema()->leafrefBaseType(path));
+            baseTypeStr = leafDataTypeToString(m_datastore.schema()->leafType(std::get<yang::LeafRef>(leafType).m_targetXPath));
         } else {
             baseTypeStr = leafDataTypeToString(leafType);
         }
diff --git a/src/leaf_data.cpp b/src/leaf_data.cpp
deleted file mode 100644
index 0d14fe3..0000000
--- a/src/leaf_data.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2020 CESNET, https://photonics.cesnet.cz/
- *
- * Written by Václav Kubernát <kubernat@cesnet.cz>
- *
-*/
-
-#include "leaf_data.hpp"
-template <>
-std::set<std::string> createSetSuggestions_class<yang::LeafDataTypes::Enum>::getSuggestions(const ParserContext& parserContext, const Schema& schema) const
-{
-    return schema.enumValues(parserContext.m_tmpListKeyLeafPath.m_location, parserContext.m_tmpListKeyLeafPath.m_node);
-}
-
-template <>
-std::set<std::string> createSetSuggestions_class<yang::LeafDataTypes::IdentityRef>::getSuggestions(const ParserContext& parserContext, const Schema& schema) const
-{
-    return schema.validIdentities(parserContext.m_tmpListKeyLeafPath.m_location, parserContext.m_tmpListKeyLeafPath.m_node, Prefixes::WhenNeeded);
-}
diff --git a/src/leaf_data.hpp b/src/leaf_data.hpp
index bbacbea..1c4176e 100644
--- a/src/leaf_data.hpp
+++ b/src/leaf_data.hpp
@@ -8,102 +8,34 @@
 #pragma once
 
 #include <boost/spirit/home/x3.hpp>
-#include "ast_values.hpp"
 #include "ast_handlers.hpp"
 #include "common_parsers.hpp"
+#include "leaf_data_type.hpp"
 #include "schema.hpp"
 namespace x3 = boost::spirit::x3;
 
-template <yang::LeafDataTypes TYPE>
+template <typename TYPE>
 struct leaf_data_class;
 
-template <yang::LeafDataTypes TYPE>
-struct createSetSuggestions_class {
-    std::set<std::string> getSuggestions(const ParserContext& ctx, const Schema& schema) const;
-
-    template <typename T, typename Iterator, typename Context>
-    void on_success(Iterator const& begin, Iterator const&, T&, Context const& context)
-    {
-        auto& parserContext = x3::get<parser_context_tag>(context);
-        const Schema& schema = parserContext.m_schema;
-
-        // Only generate completions if the type is correct so that we don't
-        // overwrite some other completions.
-        if (schema.leafType(parserContext.m_tmpListKeyLeafPath.m_location, parserContext.m_tmpListKeyLeafPath.m_node) == TYPE) {
-            parserContext.m_completionIterator = begin;
-            auto suggestions = getSuggestions(parserContext, schema);
-            std::set<Completion> res;
-            std::transform(suggestions.begin(), suggestions.end(), std::inserter(res, res.end()), [](auto it) { return Completion{it}; });
-            parserContext.m_suggestions = res;
-        }
-    }
-};
-
-template <>
-struct leaf_data_class<yang::LeafDataTypes::Enum> {
-    template <typename T, typename Iterator, typename Context>
-    void on_success(Iterator const&, Iterator const&, T& ast, Context const& context)
-    {
-        if (_pass(context) == false)
-            return;
-        auto& parserContext = x3::get<parser_context_tag>(context);
-        auto& schema = parserContext.m_schema;
-
-        if (!schema.leafEnumHasValue(parserContext.m_tmpListKeyLeafPath.m_location, parserContext.m_tmpListKeyLeafPath.m_node, ast.m_value)) {
-            _pass(context) = false;
-            parserContext.m_errorMsg = "leaf data type mismatch: Expected an enum here. Allowed values:";
-            for (const auto& it : schema.enumValues(parserContext.m_tmpListKeyLeafPath.m_location, parserContext.m_tmpListKeyLeafPath.m_node)) {
-                parserContext.m_errorMsg += " " + it;
-            }
-        }
-    }
-};
-
-template <>
-struct leaf_data_class<yang::LeafDataTypes::IdentityRef> {
-    template <typename T, typename Iterator, typename Context>
-    void on_success(Iterator const&, Iterator const&, T& ast, Context const& context)
-    {
-        auto& parserContext = x3::get<parser_context_tag>(context);
-        auto& schema = parserContext.m_schema;
-
-        ModuleValuePair pair;
-        if (ast.m_prefix) {
-            pair.first = ast.m_prefix.get().m_name;
-        }
-        pair.second = ast.m_value;
-
-        if (!schema.leafIdentityIsValid(parserContext.m_tmpListKeyLeafPath.m_location, parserContext.m_tmpListKeyLeafPath.m_node, pair)) {
-            _pass(context) = false;
-        }
-    }
-};
-
-x3::rule<leaf_data_class<yang::LeafDataTypes::Enum>, enum_> const leaf_data_enum = "leaf_data_enum";
-x3::rule<leaf_data_class<yang::LeafDataTypes::IdentityRef>, identityRef_> const leaf_data_identityRef = "leaf_data_identityRef";
-x3::rule<struct leaf_data_class<yang::LeafDataTypes::Binary>, binary_> const leaf_data_binary = "leaf_data_binary";
-x3::rule<struct leaf_data_class<yang::LeafDataTypes::Decimal>, double> const leaf_data_decimal = "leaf_data_decimal";
-x3::rule<struct leaf_data_class<yang::LeafDataTypes::String>, std::string> const leaf_data_string = "leaf_data_string";
+x3::rule<struct leaf_data_class<yang::Enum>, enum_> const leaf_data_enum = "leaf_data_enum";
+x3::rule<struct leaf_data_class<yang::IdentityRef>, identityRef_> const leaf_data_identityRef = "leaf_data_identityRef";
+x3::rule<struct leaf_data_class<yang::Binary>, binary_> const leaf_data_binary = "leaf_data_binary";
+x3::rule<struct leaf_data_class<yang::Decimal>, double> const leaf_data_decimal = "leaf_data_decimal";
+x3::rule<struct leaf_data_class<yang::String>, std::string> const leaf_data_string = "leaf_data_string";
 x3::rule<struct leaf_data_class_binary, std::string> const leaf_data_binary_data = "leaf_data_binary_data";
 x3::rule<struct leaf_data_identityRef_data_class, identityRef_> const leaf_data_identityRef_data = "leaf_data_identityRef_data";
 
-x3::rule<createSetSuggestions_class<yang::LeafDataTypes::Enum>, x3::unused_type> const createEnumSuggestions = "createEnumSuggestions";
-x3::rule<createSetSuggestions_class<yang::LeafDataTypes::IdentityRef>, x3::unused_type> const createIdentitySuggestions = "createIdentitySuggestions";
-
 using x3::char_;
 
-auto const createEnumSuggestions_def =
-    x3::eps;
-
 auto const leaf_data_enum_def =
-    createEnumSuggestions >> +char_;
+    +char_;
 
 struct bool_symbol_table : x3::symbols<bool> {
     bool_symbol_table()
     {
-    add
-        ("true", true)
-        ("false", false);
+        add
+            ("true", true)
+            ("false", false);
     }
 } const bool_symbols;
 
@@ -124,11 +56,130 @@
 auto const leaf_data_identityRef_data_def =
     -module  >> node_identifier;
 
-auto const createIdentitySuggestions_def =
-    x3::eps;
-
+// TODO: get rid of this and use leaf_data_identityRef_data directly
 auto const leaf_data_identityRef_def =
-    createIdentitySuggestions >> leaf_data_identityRef_data;
+    leaf_data_identityRef_data;
+
+template <typename It, typename Ctx, typename RCtx, typename Attr>
+struct impl_LeafData {
+    It& first;
+    It last;
+    Ctx const& ctx;
+    RCtx& rctx;
+    Attr& attr;
+    ParserContext& parserContext;
+
+    bool operator()(const yang::Binary&) const
+    {
+        return leaf_data_binary.parse(first, last, ctx, rctx, attr);
+    }
+    bool operator()(const yang::Bool&) const
+    {
+        return bool_symbols.parse(first, last, ctx, rctx, attr);
+    }
+    bool operator()(const yang::Decimal&) const
+    {
+        return x3::double_.parse(first, last, ctx, rctx, attr);
+    }
+    bool operator()(const yang::Uint8&) const
+    {
+        return x3::uint8.parse(first, last, ctx, rctx, attr);
+    }
+    bool operator()(const yang::Uint16&) const
+    {
+        return x3::uint16.parse(first, last, ctx, rctx, attr);
+    }
+    bool operator()(const yang::Uint32&) const
+    {
+        return x3::uint32.parse(first, last, ctx, rctx, attr);
+    }
+    bool operator()(const yang::Uint64&) const
+    {
+        return x3::uint64.parse(first, last, ctx, rctx, attr);
+    }
+    bool operator()(const yang::Int8&) const
+    {
+        return x3::int8.parse(first, last, ctx, rctx, attr);
+    }
+    bool operator()(const yang::Int16&) const
+    {
+        return x3::int16.parse(first, last, ctx, rctx, attr);
+    }
+    bool operator()(const yang::Int32&) const
+    {
+        return x3::int32.parse(first, last, ctx, rctx, attr);
+    }
+    bool operator()(const yang::Int64&) const
+    {
+        return x3::int64.parse(first, last, ctx, rctx, attr);
+    }
+    bool operator()(const yang::String&) const
+    {
+        return leaf_data_string.parse(first, last, ctx, rctx, attr);
+    }
+    template <typename Type>
+    void createSetSuggestions(const Type& type) const
+    {
+        parserContext.m_suggestions.clear();
+        std::transform(type.m_allowedValues.begin(),
+                type.m_allowedValues.end(),
+                std::inserter(parserContext.m_suggestions, parserContext.m_suggestions.end()),
+                [](auto it) { return Completion{it.m_value}; });
+        parserContext.m_completionIterator = first;
+    }
+    bool operator()(const yang::Enum& type) const
+    {
+        createSetSuggestions(type);
+        // leaf_data_enum will advance the iterator if it succeeds, so I have
+        // to save the iterator here, to roll it back in case the enum is
+        // invalid.
+        auto saveIter = first;
+        auto pass = leaf_data_enum.parse(first, last, ctx, rctx, attr);
+        if (!pass) {
+            return false;
+        }
+        auto isValidEnum = type.m_allowedValues.count(boost::get<enum_>(attr)) != 0;
+        if (!isValidEnum) {
+            first = saveIter;
+            parserContext.m_errorMsg = "leaf data type mismatch: Expected an enum here. Allowed values:";
+            for (const auto& it : type.m_allowedValues) {
+                parserContext.m_errorMsg += " " + it.m_value;
+            }
+        }
+        return isValidEnum;
+    }
+    bool operator()(const yang::IdentityRef& type) const
+    {
+        createSetSuggestions(type);
+        // leaf_data_identityRef will advance the iterator if it succeeds, so I have
+        // to save the iterator here, to roll it back in case the enum is
+        // invalid.
+        auto saveIter = first;
+        auto pass = leaf_data_identityRef.parse(first, last, ctx, rctx, attr);
+        if (!pass) {
+            return false;
+        }
+        identityRef_ pair{boost::get<identityRef_>(attr)};
+        if (!pair.m_prefix) {
+            pair.m_prefix = module_{parserContext.currentSchemaPath().m_nodes.front().m_prefix.get().m_name};
+        }
+        auto isValidIdentity  = type.m_allowedValues.count(pair) != 0;
+        if (!isValidIdentity) {
+            first = saveIter;
+        }
+        return isValidIdentity;
+    }
+    bool operator()(const yang::LeafRef& leafRef) const
+    {
+        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> {
     using attribute_type = leaf_data_;
@@ -141,43 +192,7 @@
         const Schema& schema = parserContext.m_schema;
         auto type = schema.leafType(parserContext.m_tmpListKeyLeafPath.m_location, parserContext.m_tmpListKeyLeafPath.m_node);
 
-        std::function<bool(yang::LeafDataTypes)> parse_impl = [&](auto type) {
-            switch (type) {
-            case yang::LeafDataTypes::Binary:
-                return leaf_data_binary.parse(first, last, ctx, rctx, attr);
-            case yang::LeafDataTypes::Bool:
-                return bool_symbols.parse(first, last, ctx, rctx, attr);
-            case yang::LeafDataTypes::Decimal:
-                return x3::double_.parse(first, last, ctx, rctx, attr);
-            case yang::LeafDataTypes::Uint8:
-                return x3::uint8.parse(first, last, ctx, rctx, attr);
-            case yang::LeafDataTypes::Uint16:
-                return x3::uint16.parse(first, last, ctx, rctx, attr);
-            case yang::LeafDataTypes::Uint32:
-                return x3::uint32.parse(first, last, ctx, rctx, attr);
-            case yang::LeafDataTypes::Uint64:
-                return x3::uint64.parse(first, last, ctx, rctx, attr);
-            case yang::LeafDataTypes::Int8:
-                return x3::int8.parse(first, last, ctx, rctx, attr);
-            case yang::LeafDataTypes::Int16:
-                return x3::int16.parse(first, last, ctx, rctx, attr);
-            case yang::LeafDataTypes::Int32:
-                return x3::int32.parse(first, last, ctx, rctx, attr);
-            case yang::LeafDataTypes::Int64:
-                return x3::int64.parse(first, last, ctx, rctx, attr);
-            case yang::LeafDataTypes::String:
-                return leaf_data_string.parse(first, last, ctx, rctx, attr);
-            case yang::LeafDataTypes::Enum:
-                return leaf_data_enum.parse(first, last, ctx, rctx, attr);
-            case yang::LeafDataTypes::IdentityRef:
-                return leaf_data_identityRef.parse(first, last, ctx, rctx, attr);
-            case yang::LeafDataTypes::LeafRef:
-                auto actualType = schema.leafrefBaseType(parserContext.m_tmpListKeyLeafPath.m_location, parserContext.m_tmpListKeyLeafPath.m_node);
-                return parse_impl(actualType);
-            }
-            __builtin_unreachable();
-        };
-        auto pass = parse_impl(type);
+        auto pass = std::visit(impl_LeafData<It, Ctx, RCtx, Attr>{first, last, ctx, rctx, attr, parserContext}, type);
 
         if (!pass) {
             if (parserContext.m_errorMsg.empty()) {
@@ -197,5 +212,3 @@
 BOOST_SPIRIT_DEFINE(leaf_data_binary)
 BOOST_SPIRIT_DEFINE(leaf_data_identityRef_data)
 BOOST_SPIRIT_DEFINE(leaf_data_identityRef)
-BOOST_SPIRIT_DEFINE(createEnumSuggestions)
-BOOST_SPIRIT_DEFINE(createIdentitySuggestions)
diff --git a/src/leaf_data_type.cpp b/src/leaf_data_type.cpp
new file mode 100644
index 0000000..8048efa
--- /dev/null
+++ b/src/leaf_data_type.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2020 CESNET, https://photonics.cesnet.cz/
+ *
+ * Written by Václav Kubernát <kubernat@cesnet.cz>
+ *
+*/
+#include "ast_values.hpp"
+#include "leaf_data_type.hpp"
+
+namespace yang {
+Enum::Enum(std::set<enum_>&& values)
+    : m_allowedValues(std::move(values))
+{
+}
+bool Enum::operator==(const Enum& other) const
+{
+    return this->m_allowedValues == other.m_allowedValues;
+}
+IdentityRef::IdentityRef(std::set<identityRef_>&& values)
+    : m_allowedValues(std::move(values))
+{
+}
+bool IdentityRef::operator==(const IdentityRef& other) const
+{
+    return this->m_allowedValues == other.m_allowedValues;
+}
+// Copy constructor needed, because unique_ptr is not copy-constructible
+LeafRef::LeafRef(const LeafRef& src)
+    : m_targetXPath(src.m_targetXPath)
+    , m_targetType(std::make_unique<LeafDataType>(*src.m_targetType))
+{
+}
+LeafRef::LeafRef(const std::string& xpath, std::unique_ptr<LeafDataType>&& type)
+    : m_targetXPath(xpath)
+    , m_targetType(std::move(type))
+{
+}
+bool LeafRef::operator==(const LeafRef& other) const
+{
+    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;
+}
+bool Decimal::operator==(const Decimal&) const
+{
+    return true;
+}
+bool Bool::operator==(const Bool&) const
+{
+    return true;
+}
+bool Int8::operator==(const Int8&) const
+{
+    return true;
+}
+bool Uint8::operator==(const Uint8&) const
+{
+    return true;
+}
+bool Int16::operator==(const Int16&) const
+{
+    return true;
+}
+bool Uint16::operator==(const Uint16&) const
+{
+    return true;
+}
+bool Int32::operator==(const Int32&) const
+{
+    return true;
+}
+bool Uint32::operator==(const Uint32&) const
+{
+    return true;
+}
+bool Int64::operator==(const Int64&) const
+{
+    return true;
+}
+bool Uint64::operator==(const Uint64&) const
+{
+    return true;
+}
+bool Binary::operator==(const Binary&) const
+{
+    return true;
+}
+}
diff --git a/src/leaf_data_type.hpp b/src/leaf_data_type.hpp
new file mode 100644
index 0000000..a86691b
--- /dev/null
+++ b/src/leaf_data_type.hpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2020 CESNET, https://photonics.cesnet.cz/
+ *
+ * Written by Václav Kubernát <kubernat@cesnet.cz>
+ *
+*/
+
+#pragma once
+#include <memory>
+#include <set>
+#include <string>
+#include <variant>
+#include <vector>
+
+struct enum_;
+struct identityRef_;
+
+namespace yang {
+struct String {
+    bool operator==(const String&) const;
+};
+struct Decimal {
+    bool operator==(const Decimal&) const;
+};
+struct Bool {
+    bool operator==(const Bool&) const;
+};
+struct Int8 {
+    bool operator==(const Int8&) const;
+};
+struct Uint8 {
+    bool operator==(const Uint8&) const;
+};
+struct Int16 {
+    bool operator==(const Int16&) const;
+};
+struct Uint16 {
+    bool operator==(const Uint16&) const;
+};
+struct Int32 {
+    bool operator==(const Int32&) const;
+};
+struct Uint32 {
+    bool operator==(const Uint32&) const;
+};
+struct Int64 {
+    bool operator==(const Int64&) const;
+};
+struct Uint64 {
+    bool operator==(const Uint64&) const;
+};
+struct Binary {
+    bool operator==(const Binary&) const;
+};
+struct Enum {
+    Enum(std::set<enum_>&& values);
+    bool operator==(const Enum& other) const;
+    std::set<enum_> m_allowedValues;
+};
+struct IdentityRef {
+    IdentityRef(std::set<identityRef_>&& list);
+    bool operator==(const IdentityRef& other) const;
+    std::set<identityRef_> m_allowedValues;
+};
+struct LeafRef;
+struct Union;
+using LeafDataType = std::variant<
+    yang::String,
+    yang::Decimal,
+    yang::Bool,
+    yang::Int8,
+    yang::Uint8,
+    yang::Int16,
+    yang::Uint16,
+    yang::Int32,
+    yang::Uint32,
+    yang::Int64,
+    yang::Uint64,
+    yang::Enum,
+    yang::Binary,
+    yang::IdentityRef,
+    yang::LeafRef,
+    yang::Union
+>;
+struct LeafRef {
+    LeafRef(const LeafRef& src);
+    LeafRef(const std::string& xpath, std::unique_ptr<LeafDataType>&& type);
+    bool operator==(const LeafRef& other) const;
+    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/parser_context.hpp b/src/parser_context.hpp
index 3ad0179..f837934 100644
--- a/src/parser_context.hpp
+++ b/src/parser_context.hpp
@@ -31,6 +31,7 @@
         schemaPath_ m_location;
         ModuleNodePair m_node;
     } m_tmpListKeyLeafPath;
+
     std::map<std::string, leaf_data_> m_tmpListKeys;
     bool m_errorHandled = false;
     bool m_completing = false;
diff --git a/src/schema.hpp b/src/schema.hpp
index ea0deba..9a86045 100644
--- a/src/schema.hpp
+++ b/src/schema.hpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright (C) 2018 CESNET, https://photonics.cesnet.cz/
  * Copyright (C) 2018 FIT CVUT, https://fit.cvut.cz/
@@ -14,28 +13,11 @@
 #include <stdexcept>
 #include <unordered_map>
 #include "ast_path.hpp"
+#include "leaf_data_type.hpp"
 
 using ModuleValuePair = std::pair<boost::optional<std::string>, std::string>;
 
 namespace yang {
-enum class LeafDataTypes {
-    String,
-    Decimal,
-    Bool,
-    Int8,
-    Uint8,
-    Int16,
-    Uint16,
-    Int32,
-    Uint32,
-    Int64,
-    Uint64,
-    Enum,
-    Binary,
-    IdentityRef,
-    LeafRef,
-};
-
 enum class NodeTypes {
     Container,
     PresenceContainer,
@@ -70,22 +52,16 @@
     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 leafEnumHasValue(const schemaPath_& location, const ModuleNodePair& node, const std::string& value) const = 0;
-    virtual bool leafIdentityIsValid(const schemaPath_& location, const ModuleNodePair& node, const ModuleValuePair& value) const = 0;
     virtual bool listHasKey(const schemaPath_& location, const ModuleNodePair& node, const std::string& key) const = 0;
     virtual bool leafIsKey(const std::string& leafPath) const = 0;
     virtual const std::set<std::string> listKeys(const schemaPath_& location, const ModuleNodePair& node) const = 0;
-    virtual yang::LeafDataTypes leafType(const schemaPath_& location, const ModuleNodePair& node) const = 0;
-    virtual yang::LeafDataTypes leafType(const std::string& path) const = 0;
+    virtual yang::LeafDataType leafType(const schemaPath_& location, const ModuleNodePair& node) const = 0;
+    virtual yang::LeafDataType leafType(const std::string& path) const = 0;
     virtual std::optional<std::string> leafTypeName(const std::string& path) const = 0;
-    virtual yang::LeafDataTypes leafrefBaseType(const schemaPath_& location, const ModuleNodePair& node) const = 0;
-    virtual yang::LeafDataTypes leafrefBaseType(const std::string& path) const = 0;
     virtual std::string leafrefPath(const std::string& leafrefPath) const = 0;
     virtual std::optional<std::string> description(const std::string& location) const = 0;
     virtual std::optional<std::string> units(const std::string& location) const = 0;
 
-    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 87d190a..3eb42f6 100644
--- a/src/static_schema.cpp
+++ b/src/static_schema.cpp
@@ -71,40 +71,22 @@
     m_nodes.emplace(key, std::unordered_map<std::string, NodeType>());
 }
 
-void StaticSchema::addLeaf(const std::string& location, const std::string& name, const yang::LeafDataTypes& type)
+std::set<identityRef_> StaticSchema::validIdentities(std::string_view module, std::string_view value)
 {
-    m_nodes.at(location).emplace(name, yang::leaf{type, {}, {}, {}});
-    std::string key = joinPaths(location, name);
-    m_nodes.emplace(key, std::unordered_map<std::string, NodeType>());
+    std::set<ModuleValuePair> identities;
+    getIdentSet(ModuleNodePair{boost::optional<std::string>{module}, value}, identities);
+    std::set<identityRef_> res;
+
+    std::transform(identities.begin(), identities.end(), std::inserter(res, res.end()), [](const auto& identity) {
+        return identityRef_{*identity.first, identity.second};
+    });
+
+    return res;
 }
 
-void StaticSchema::addLeafEnum(const std::string& location, const std::string& name, std::set<std::string> enumValues)
+void StaticSchema::addLeaf(const std::string& location, const std::string& name, const yang::LeafDataType& type)
 {
-    yang::leaf toAdd;
-    toAdd.m_type = yang::LeafDataTypes::Enum;
-    toAdd.m_enumValues = enumValues;
-    m_nodes.at(location).emplace(name, toAdd);
-    std::string key = joinPaths(location, name);
-    m_nodes.emplace(key, std::unordered_map<std::string, NodeType>());
-}
-
-void StaticSchema::addLeafIdentityRef(const std::string& location, const std::string& name, const ModuleValuePair& base)
-{
-    assert(base.first); // base identity cannot have an empty module
-    yang::leaf toAdd;
-    toAdd.m_type = yang::LeafDataTypes::IdentityRef;
-    toAdd.m_identBase = base;
-    m_nodes.at(location).emplace(name, toAdd);
-    std::string key = joinPaths(location, name);
-    m_nodes.emplace(key, std::unordered_map<std::string, NodeType>());
-}
-
-void StaticSchema::addLeafRef(const std::string& location, const std::string& name, const std::string& source)
-{
-    yang::leaf toAdd;
-    toAdd.m_type = yang::LeafDataTypes::LeafRef;
-    toAdd.m_leafRefSource = source;
-    m_nodes.at(location).emplace(name, toAdd);
+    m_nodes.at(location).emplace(name, yang::leaf{type});
     std::string key = joinPaths(location, name);
     m_nodes.emplace(key, std::unordered_map<std::string, NodeType>());
 }
@@ -122,12 +104,6 @@
     m_identities.emplace(name, std::set<ModuleValuePair>());
 }
 
-bool StaticSchema::leafEnumHasValue(const schemaPath_& location, const ModuleNodePair& node, const std::string& value) const
-{
-    auto enums = enumValues(location, node);
-    return enums.find(value) != enums.end();
-}
-
 void StaticSchema::getIdentSet(const ModuleValuePair& ident, std::set<ModuleValuePair>& res) const
 {
     res.insert(ident);
@@ -137,86 +113,26 @@
     }
 }
 
-const std::set<std::string> StaticSchema::validIdentities(const schemaPath_& location, const ModuleNodePair& node, const Prefixes prefixes) const
-{
-    std::string locationString = pathToSchemaString(location, Prefixes::Always);
-    assert(isLeaf(location, node));
-
-    const auto& child = children(locationString).at(fullNodeName(location, node));
-    const auto& leaf = boost::get<yang::leaf>(child);
-
-    std::set<ModuleValuePair> identSet;
-    getIdentSet(leaf.m_identBase, identSet);
-
-    std::set<std::string> res;
-    std::transform(identSet.begin(), identSet.end(), std::inserter(res, res.end()), [location, node, prefixes](const auto& it) {
-        auto topLevelModule = location.m_nodes.empty() ? node.first.get() : location.m_nodes.front().m_prefix.get().m_name;
-        std::string stringIdent;
-        if (prefixes == Prefixes::Always || (it.first && it.first.value() != topLevelModule)) {
-            stringIdent += it.first ? it.first.value() : topLevelModule;
-            stringIdent += ":";
-        }
-        stringIdent += it.second;
-        return stringIdent;
-    });
-
-    return res;
-}
-
-bool StaticSchema::leafIdentityIsValid(const schemaPath_& location, const ModuleNodePair& node, const ModuleValuePair& value) const
-{
-    auto identities = validIdentities(location, node, Prefixes::Always);
-
-    auto topLevelModule = location.m_nodes.empty() ? node.first.get() : location.m_nodes.front().m_prefix.get().m_name;
-    auto identModule = value.first ? value.first.value() : topLevelModule;
-    return std::any_of(identities.begin(), identities.end(), [toFind = identModule + ":" + value.second](const auto& x) { return x == toFind; });
-}
-
 std::string lastNodeOfSchemaPath(const std::string& path)
 {
     std::string res = path;
-    auto pos = res.find_last_of('/');
-    if (pos == 0) { // path had only one path fragment - "/something:something"
-        res.erase(0, 1);
-        return res;
+    if (auto pos = res.find_last_of('/'); pos != res.npos) {
+        res.erase(0, pos + 1);
     }
-    if (pos != res.npos) { // path had more fragments
-        res.erase(0, pos);
-        return res;
-    }
-
-    // path was empty
     return res;
 }
 
-yang::LeafDataTypes StaticSchema::leafrefBaseType(const schemaPath_& location, const ModuleNodePair& node) const
-{
-    std::string locationString = pathToSchemaString(location, Prefixes::Always);
-    auto leaf{boost::get<yang::leaf>(children(locationString).at(fullNodeName(location, node)))};
-    auto locationOfSource = stripLastNodeFromPath(leaf.m_leafRefSource);
-    auto nameOfSource = lastNodeOfSchemaPath(leaf.m_leafRefSource);
-    return boost::get<yang::leaf>(children(locationOfSource).at(nameOfSource)).m_type;
-}
-
-yang::LeafDataTypes StaticSchema::leafType(const schemaPath_& location, const ModuleNodePair& node) const
+yang::LeafDataType StaticSchema::leafType(const schemaPath_& location, const ModuleNodePair& node) const
 {
     std::string locationString = pathToSchemaString(location, Prefixes::Always);
     return boost::get<yang::leaf>(children(locationString).at(fullNodeName(location, node))).m_type;
 }
 
-yang::LeafDataTypes StaticSchema::leafType([[maybe_unused]] const std::string& path) const
+yang::LeafDataType StaticSchema::leafType(const std::string& path) const
 {
-    throw std::runtime_error{"StaticSchema::leafType not implemented"};
-}
-
-const std::set<std::string> StaticSchema::enumValues(const schemaPath_& location, const ModuleNodePair& node) const
-{
-    std::string locationString = pathToSchemaString(location, Prefixes::Always);
-    assert(isLeaf(location, node));
-
-    const auto& child = children(locationString).at(fullNodeName(location, node));
-    const auto& leaf = boost::get<yang::leaf>(child);
-    return leaf.m_enumValues;
+    auto locationString = stripLastNodeFromPath(path);
+    auto node = lastNodeOfSchemaPath(path);
+    return boost::get<yang::leaf>(children(locationString).at(node)).m_type;
 }
 
 // We do not test StaticSchema, so we don't need to implement recursive childNodes
@@ -291,11 +207,6 @@
     throw std::runtime_error{"Internal error: StaticSchema::nodeType(std::string) not implemented. The tests should not have called this overload."};
 }
 
-yang::LeafDataTypes StaticSchema::leafrefBaseType([[maybe_unused]] const std::string& path) const
-{
-    throw std::runtime_error{"Internal error: StaticSchema::leafrefBaseType(std::string) not implemented. The tests should not have called this overload."};
-}
-
 std::string StaticSchema::leafrefPath([[maybe_unused]] const std::string& leafrefPath) const
 {
     throw std::runtime_error{"Internal error: StaticSchema::leafrefPath(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 97f1e7e..a94c78d 100644
--- a/src/static_schema.hpp
+++ b/src/static_schema.hpp
@@ -27,10 +27,7 @@
 };
 
 struct leaf {
-    yang::LeafDataTypes m_type;
-    std::set<std::string> m_enumValues;
-    ModuleValuePair m_identBase;
-    std::string m_leafRefSource;
+    yang::LeafDataType m_type;
 };
 
 struct module {
@@ -51,29 +48,24 @@
     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 leafEnumHasValue(const schemaPath_& location, const ModuleNodePair& node, const std::string& value) const override;
-    bool leafIdentityIsValid(const schemaPath_& location, const ModuleNodePair& node, const ModuleValuePair& value) const override;
     bool listHasKey(const schemaPath_& location, const ModuleNodePair& node, const std::string& key) const override;
     bool leafIsKey(const std::string& leafPath) const override;
     const std::set<std::string> listKeys(const schemaPath_& location, const ModuleNodePair& node) const override;
-    yang::LeafDataTypes leafType(const schemaPath_& location, const ModuleNodePair& node) const override;
-    yang::LeafDataTypes leafType(const std::string& path) const override;
+    yang::LeafDataType leafType(const schemaPath_& location, const ModuleNodePair& node) const override;
+    yang::LeafDataType leafType(const std::string& path) const override;
     std::optional<std::string> leafTypeName(const std::string& path) const override;
-    yang::LeafDataTypes leafrefBaseType(const schemaPath_& location, const ModuleNodePair& node) const override;
-    yang::LeafDataTypes leafrefBaseType(const std::string& path) const override;
     std::string leafrefPath(const std::string& leafrefPath) const override;
-    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;
     std::optional<std::string> description(const std::string& path) const override;
     std::optional<std::string> units(const std::string& path) const override;
 
+    /** A helper for making tests a little bit easier. It returns all
+     * identities which are based on the argument passed and which can then be
+     * used in addLeaf for the `type` argument */
+    std::set<identityRef_> validIdentities(std::string_view module, std::string_view value);
     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);
-    void addLeafEnum(const std::string& location, const std::string& name, std::set<std::string> enumValues);
-    void addLeafIdentityRef(const std::string& location, const std::string& name, const ModuleValuePair& base);
-    void addLeafRef(const std::string& location, const std::string& name, const std::string& source);
+    void addLeaf(const std::string& location, const std::string& name, const yang::LeafDataType& type);
     void addList(const std::string& location, const std::string& name, const std::set<std::string>& keys);
     void addModule(const std::string& name);
     void addIdentity(const std::optional<ModuleValuePair>& base, const ModuleValuePair& name);
@@ -85,5 +77,7 @@
 
     std::unordered_map<std::string, std::unordered_map<std::string, NodeType>> m_nodes;
     std::set<std::string> m_modules;
+
+    // FIXME: Change the template arguments to identityRef_
     std::map<ModuleValuePair, std::set<ModuleValuePair>> m_identities;
 };
diff --git a/src/utils.cpp b/src/utils.cpp
index fcbe0d0..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"
@@ -52,42 +53,80 @@
     return schemaPath_{path.m_scope, decltype(schemaPath_::m_nodes)(path.m_nodes.begin(), path.m_nodes.end() - 1)};
 }
 
-std::string leafDataTypeToString(yang::LeafDataTypes type)
-{
-    switch (type) {
-    case yang::LeafDataTypes::String:
+struct impl_leafDataTypeToString {
+    std::string operator()(const yang::String)
+    {
         return "a string";
-    case yang::LeafDataTypes::Decimal:
-        return "a decimal";
-    case yang::LeafDataTypes::Bool:
-        return "a boolean";
-    case yang::LeafDataTypes::Int8:
-        return "an 8-bit integer";
-    case yang::LeafDataTypes::Uint8:
-        return "an 8-bit unsigned integer";
-    case yang::LeafDataTypes::Int16:
-        return "a 16-bit integer";
-    case yang::LeafDataTypes::Uint16:
-        return "a 16-bit unsigned integer";
-    case yang::LeafDataTypes::Int32:
-        return "a 32-bit integer";
-    case yang::LeafDataTypes::Uint32:
-        return "a 32-bit unsigned integer";
-    case yang::LeafDataTypes::Int64:
-        return "a 64-bit integer";
-    case yang::LeafDataTypes::Uint64:
-        return "a 64-bit unsigned integer";
-    case yang::LeafDataTypes::Enum:
-        return "an enum";
-    case yang::LeafDataTypes::IdentityRef:
-        return "an identity";
-    case yang::LeafDataTypes::LeafRef:
-        return "a leafref";
-    case yang::LeafDataTypes::Binary:
-        return "a base64-encoded binary value";
-    default:
-        throw std::runtime_error("leafDataTypeToString: unsupported leaf data type");
     }
+    std::string operator()(const yang::Decimal)
+    {
+        return "a decimal";
+    }
+    std::string operator()(const yang::Bool)
+    {
+        return "a boolean";
+    }
+    std::string operator()(const yang::Int8)
+    {
+        return "an 8-bit integer";
+    }
+    std::string operator()(const yang::Uint8)
+    {
+        return "an 8-bit unsigned integer";
+    }
+    std::string operator()(const yang::Int16)
+    {
+        return "a 16-bit integer";
+    }
+    std::string operator()(const yang::Uint16)
+    {
+        return "a 16-bit unsigned integer";
+    }
+    std::string operator()(const yang::Int32)
+    {
+        return "a 32-bit integer";
+    }
+    std::string operator()(const yang::Uint32)
+    {
+        return "a 32-bit unsigned integer";
+    }
+    std::string operator()(const yang::Int64)
+    {
+        return "a 64-bit integer";
+    }
+    std::string operator()(const yang::Uint64)
+    {
+        return "a 64-bit unsigned integer";
+    }
+    std::string operator()(const yang::Binary)
+    {
+        return "a base64-encoded binary value";
+    }
+    std::string operator()(const yang::Enum&)
+    {
+        return "an enum";
+    }
+    std::string operator()(const yang::IdentityRef&)
+    {
+        return "an identity";
+    }
+    std::string operator()(const yang::LeafRef&)
+    {
+        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)
+{
+    return std::visit(impl_leafDataTypeToString{}, type);
 }
 
 std::string fullNodeName(const schemaPath_& location, const ModuleNodePair& pair)
diff --git a/src/utils.hpp b/src/utils.hpp
index f0f7ae5..2f93bfb 100644
--- a/src/utils.hpp
+++ b/src/utils.hpp
@@ -17,7 +17,7 @@
 std::string stripLastNodeFromPath(const std::string& path);
 schemaPath_ pathWithoutLastNode(const schemaPath_& path);
 dataPath_ pathWithoutLastNode(const dataPath_& path);
-std::string leafDataTypeToString(yang::LeafDataTypes type);
+std::string leafDataTypeToString(const yang::LeafDataType& type);
 std::string fullNodeName(const schemaPath_& location, const ModuleNodePair& pair);
 std::string fullNodeName(const dataPath_& location, const ModuleNodePair& pair);
 std::string leafDataToString(const leaf_data_ value);
diff --git a/src/yang_schema.cpp b/src/yang_schema.cpp
index e18c780..a453b50 100644
--- a/src/yang_schema.cpp
+++ b/src/yang_schema.cpp
@@ -93,80 +93,6 @@
     return set.find(name) != set.end();
 }
 
-bool YangSchema::leafEnumHasValue(const schemaPath_& location, const ModuleNodePair& node, const std::string& value) const
-{
-    auto enums = enumValues(location, node);
-
-    return std::any_of(enums.begin(), enums.end(), [=](const auto& x) { return x == value; });
-}
-
-const std::set<std::string> YangSchema::enumValues(const schemaPath_& location, const ModuleNodePair& node) const
-{
-    if (!isLeaf(location, node) || leafType(location, node) != yang::LeafDataTypes::Enum)
-        return {};
-
-    libyang::Schema_Node_Leaf leaf(getSchemaNode(location, node));
-    auto type = leaf.type();
-    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
-    // up the hierarchy until we find a typedef that defined these values.
-    while (enm.empty()) {
-        type = type->der()->type();
-        enm = type->info()->enums()->enm();
-    }
-
-    std::vector<libyang::S_Type_Enum> enabled;
-    std::copy_if(enm.begin(), enm.end(), std::back_inserter(enabled), [](const libyang::S_Type_Enum& it) {
-        auto iffeatures = it->iffeature();
-        return std::all_of(iffeatures.begin(), iffeatures.end(), [](auto it) { return it->value(); });
-    });
-
-    std::set<std::string> enumSet;
-    std::transform(enabled.begin(), enabled.end(), std::inserter(enumSet, enumSet.end()), [](auto it) { return it->name(); });
-    return enumSet;
-}
-
-const std::set<std::string> YangSchema::validIdentities(const schemaPath_& location, const ModuleNodePair& node, const Prefixes prefixes) const
-{
-    if (!isLeaf(location, node) || leafType(location, node) != yang::LeafDataTypes::IdentityRef)
-        return {};
-
-    std::set<std::string> identSet;
-
-    auto topLevelModule = location.m_nodes.empty() ? node.first.get() : location.m_nodes.front().m_prefix.get().m_name;
-    auto insertToSet = [&identSet, prefixes, topLevelModule](auto module, auto name) {
-        std::string stringIdent;
-        if (prefixes == Prefixes::Always || topLevelModule != module) {
-            stringIdent += module;
-            stringIdent += ":";
-        }
-        stringIdent += name;
-        identSet.emplace(stringIdent);
-    };
-
-    auto leaf = std::make_shared<libyang::Schema_Node_Leaf>(getSchemaNode(location, node));
-    auto info = leaf->type()->info();
-    for (auto base : info->ident()->ref()) { // Iterate over all bases
-        insertToSet(base->module()->name(), base->name());
-        // Iterate over derived identities (this is recursive!)
-        for (auto derived : base->der()->schema()) {
-            insertToSet(derived->module()->name(), derived->name());
-        }
-    }
-
-    return identSet;
-}
-
-bool YangSchema::leafIdentityIsValid(const schemaPath_& location, const ModuleNodePair& node, const ModuleValuePair& value) const
-{
-    auto identities = validIdentities(location, node, Prefixes::Always);
-
-    auto topLevelModule = location.m_nodes.empty() ? node.first.get() : location.m_nodes.front().m_prefix.get().m_name;
-    auto identModule = value.first ? value.first.value() : topLevelModule;
-    return std::any_of(identities.begin(), identities.end(), [toFind = identModule + ":" + value.second](const auto& x) { return x == toFind; });
-}
-
 bool YangSchema::listHasKey(const schemaPath_& location, const ModuleNodePair& node, const std::string& key) const
 {
     if (!isList(location, node))
@@ -227,65 +153,113 @@
     return keys;
 }
 
-yang::LeafDataTypes lyTypeToLeafDataTypes(LY_DATA_TYPE type)
+namespace {
+std::set<enum_> enumValues(const libyang::S_Type& typeArg)
 {
-    switch (type) {
-    case LY_TYPE_STRING:
-        return yang::LeafDataTypes::String;
-    case LY_TYPE_DEC64:
-        return yang::LeafDataTypes::Decimal;
-    case LY_TYPE_BOOL:
-        return yang::LeafDataTypes::Bool;
-    case LY_TYPE_INT8:
-        return yang::LeafDataTypes::Int8;
-    case LY_TYPE_INT16:
-        return yang::LeafDataTypes::Int16;
-    case LY_TYPE_INT32:
-        return yang::LeafDataTypes::Int32;
-    case LY_TYPE_INT64:
-        return yang::LeafDataTypes::Int64;
-    case LY_TYPE_UINT8:
-        return yang::LeafDataTypes::Uint8;
-    case LY_TYPE_UINT16:
-        return yang::LeafDataTypes::Uint16;
-    case LY_TYPE_UINT32:
-        return yang::LeafDataTypes::Uint32;
-    case LY_TYPE_UINT64:
-        return yang::LeafDataTypes::Uint64;
-    case LY_TYPE_ENUM:
-        return yang::LeafDataTypes::Enum;
-    case LY_TYPE_BINARY:
-        return yang::LeafDataTypes::Binary;
-    case LY_TYPE_IDENT:
-        return yang::LeafDataTypes::IdentityRef;
-    case LY_TYPE_LEAFREF:
-        return yang::LeafDataTypes::LeafRef;
-    default:
-        using namespace std::string_literals;
-        throw std::logic_error{"internal error: unsupported libyang data type "s + std::to_string(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
+    // up the hierarchy until we find a typedef that defined these values.
+    while (enm.empty()) {
+        type = type->der()->type();
+        enm = type->info()->enums()->enm();
     }
+
+    std::vector<libyang::S_Type_Enum> enabled;
+    std::copy_if(enm.begin(), enm.end(), std::back_inserter(enabled), [](const libyang::S_Type_Enum& it) {
+        auto iffeatures = it->iffeature();
+        return std::all_of(iffeatures.begin(), iffeatures.end(), [](auto it) { return it->value(); });
+    });
+
+    std::set<enum_> enumSet;
+    std::transform(enabled.begin(), enabled.end(), std::inserter(enumSet, enumSet.end()), [](auto it) { return enum_{it->name()}; });
+    return enumSet;
 }
 
-namespace {
-yang::LeafDataTypes impl_leafType(const libyang::S_Schema_Node& node)
+std::set<identityRef_> validIdentities(const libyang::S_Type& type)
+{
+    std::set<identityRef_> identSet;
+
+    // auto topLevelModule = leaf->module();
+
+    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!)
+        for (auto derived : base->der()->schema()) {
+            identSet.emplace(derived->module()->name(), derived->name());
+        }
+    }
+
+    return identSet;
+}
+
+std::string leafrefPath(const libyang::S_Type& type)
+{
+    return type->info()->lref()->target()->path(LYS_PATH_FIRST_PREFIX);
+}
+}
+
+yang::LeafDataType YangSchema::impl_leafType(const libyang::S_Schema_Node& node) const
 {
     using namespace std::string_literals;
-    libyang::Schema_Node_Leaf leaf(node);
-    auto baseType{leaf.type()->base()};
-    try {
-        return lyTypeToLeafDataTypes(baseType);
-    } catch (std::logic_error& ex) {
-        throw UnsupportedYangTypeException("the type of "s + node->name() + " is not supported: " + ex.what());
-    }
-}
+    auto leaf = std::make_shared<libyang::Schema_Node_Leaf>(node);
+    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::LeafDataTypes YangSchema::leafType(const schemaPath_& location, const ModuleNodePair& node) const
+yang::LeafDataType YangSchema::leafType(const schemaPath_& location, const ModuleNodePair& node) const
 {
     return impl_leafType(getSchemaNode(location, node));
 }
 
-yang::LeafDataTypes YangSchema::leafType(const std::string& path) const
+yang::LeafDataType YangSchema::leafType(const std::string& path) const
 {
     return impl_leafType(getSchemaNode(path));
 }
@@ -296,29 +270,6 @@
     return leaf.type()->der().get() ? std::optional{leaf.type()->der()->name()} : std::nullopt;
 }
 
-namespace {
-yang::LeafDataTypes impl_leafrefBaseType(const libyang::S_Schema_Node& node)
-{
-    using namespace std::string_literals;
-    libyang::Schema_Node_Leaf leaf(node);
-    try {
-        return lyTypeToLeafDataTypes(leaf.type()->info()->lref()->target()->type()->base());
-    } catch (std::logic_error& ex) {
-        throw UnsupportedYangTypeException("the type of "s + node->name() + " is not supported: " + ex.what());
-    }
-}
-}
-
-yang::LeafDataTypes YangSchema::leafrefBaseType(const schemaPath_& location, const ModuleNodePair& node) const
-{
-    return impl_leafrefBaseType(getSchemaNode(location, node));
-}
-
-yang::LeafDataTypes YangSchema::leafrefBaseType(const std::string& path) const
-{
-    return impl_leafrefBaseType(getSchemaNode(path));
-}
-
 std::string YangSchema::leafrefPath(const std::string& leafrefPath) const
 {
     using namespace std::string_literals;
diff --git a/src/yang_schema.hpp b/src/yang_schema.hpp
index c763ea6..e3a9025 100644
--- a/src/yang_schema.hpp
+++ b/src/yang_schema.hpp
@@ -17,6 +17,7 @@
 namespace libyang {
 class Context;
 class Schema_Node;
+class Schema_Node_Leaf;
 class Data_Node;
 class Module;
 }
@@ -33,19 +34,13 @@
     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 leafEnumHasValue(const schemaPath_& location, const ModuleNodePair& node, const std::string& value) const override;
-    bool leafIdentityIsValid(const schemaPath_& location, const ModuleNodePair& node, const ModuleValuePair& value) const override;
     bool listHasKey(const schemaPath_& location, const ModuleNodePair& node, const std::string& key) const override;
     bool leafIsKey(const std::string& leafPath) const override;
     const std::set<std::string> listKeys(const schemaPath_& location, const ModuleNodePair& node) const override;
-    yang::LeafDataTypes leafType(const schemaPath_& location, const ModuleNodePair& node) const override;
-    yang::LeafDataTypes leafType(const std::string& path) const override;
+    yang::LeafDataType leafType(const schemaPath_& location, const ModuleNodePair& node) const override;
+    yang::LeafDataType leafType(const std::string& path) const override;
     std::optional<std::string> leafTypeName(const std::string& path) const override;
-    yang::LeafDataTypes leafrefBaseType(const schemaPath_& location, const ModuleNodePair& node) const override;
-    yang::LeafDataTypes leafrefBaseType(const std::string& path) const override;
     std::string leafrefPath(const std::string& leafrefPath) const override;
-    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;
     std::optional<std::string> description(const std::string& path) const override;
@@ -73,6 +68,7 @@
     std::shared_ptr<libyang::Module> getYangModule(const std::string& name);
 
 private:
+    yang::LeafDataType impl_leafType(const std::shared_ptr<libyang::Schema_Node>& node) const;
     std::set<std::string> modules() const;
 
     /** @short Returns a set of nodes, that match the location and name criteria. */
diff --git a/tests/cd.cpp b/tests/cd.cpp
index def2bdd..32d7230 100644
--- a/tests/cd.cpp
+++ b/tests/cd.cpp
@@ -24,11 +24,11 @@
     schema->addContainer("/example:a/example:a2", "example:a3");
     schema->addContainer("/example:b/example:b2", "example:b3");
     schema->addList("/", "example:list", {"number"});
-    schema->addLeaf("/example:list", "example:number", yang::LeafDataTypes::Int32);
+    schema->addLeaf("/example:list", "example:number", yang::Int32{});
     schema->addContainer("/example:list", "example:contInList");
     schema->addList("/", "example:twoKeyList", {"number", "name"});
-    schema->addLeaf("/example:twoKeyList", "example:number", yang::LeafDataTypes::Int32);
-    schema->addLeaf("/example:twoKeyList", "example:name", yang::LeafDataTypes::String);
+    schema->addLeaf("/example:twoKeyList", "example:number", yang::Int32{});
+    schema->addLeaf("/example:twoKeyList", "example:name", yang::String{});
     Parser parser(schema);
     std::string input;
     std::ostringstream errorStream;
diff --git a/tests/enum_completion.cpp b/tests/enum_completion.cpp
index 939243f..07ea32c 100644
--- a/tests/enum_completion.cpp
+++ b/tests/enum_completion.cpp
@@ -9,6 +9,7 @@
 
 #include "trompeloeil_doctest.hpp"
 #include "datastoreaccess_mock.hpp"
+#include "leaf_data_helpers.hpp"
 #include "parser.hpp"
 #include "pretty_printers.hpp"
 #include "static_schema.hpp"
@@ -18,11 +19,11 @@
     auto schema = std::make_shared<StaticSchema>();
     schema->addModule("mod");
     schema->addContainer("/", "mod:contA");
-    schema->addLeafEnum("/", "mod:leafEnum", {"lala", "lol", "data", "coze"});
-    schema->addLeafEnum("/mod:contA", "mod:leafInCont", {"abc", "def"});
+    schema->addLeaf("/", "mod:leafEnum", createEnum({"lala", "lol", "data", "coze"}));
+    schema->addLeaf("/mod:contA", "mod:leafInCont", createEnum({"abc", "def"}));
     schema->addList("/", "mod:list", {"number"});
-    schema->addLeaf("/mod:list", "mod:number", yang::LeafDataTypes::Int32);
-    schema->addLeafEnum("/mod:list", "mod:leafInList", {"ano", "anoda", "ne", "katoda"});
+    schema->addLeaf("/mod:list", "mod:number", yang::Int32{});
+    schema->addLeaf("/mod:list", "mod:leafInList", createEnum({"ano", "anoda", "ne", "katoda"}));
     auto mockDatastore = std::make_shared<MockDatastoreAccess>();
     // The parser will use DataQuery for key value completion, but I'm not testing that here, so I don't return anything.
     ALLOW_CALL(*mockDatastore, listInstances("/mod:list"))
diff --git a/tests/example-schema.yang b/tests/example-schema.yang
index f876779..5ef67d5 100644
--- a/tests/example-schema.yang
+++ b/tests/example-schema.yang
@@ -80,6 +80,13 @@
         }
     }
 
+    leaf unionIntString {
+        type union {
+            type int32;
+            type string;
+        }
+    }
+
     grouping upAndDown {
         leaf up {
             type boolean;
diff --git a/tests/keyvalue_completion.cpp b/tests/keyvalue_completion.cpp
index c6a2350..61e90cd 100644
--- a/tests/keyvalue_completion.cpp
+++ b/tests/keyvalue_completion.cpp
@@ -30,10 +30,10 @@
     schema->addContainer("/", "example:a");
     schema->addContainer("/", "example:b");
     schema->addList("/", "example:list", {"number"});
-    schema->addLeaf("/example:list", "example:number", yang::LeafDataTypes::Int32);
+    schema->addLeaf("/example:list", "example:number", yang::Int32{});
     schema->addList("/", "example:twoKeyList", {"number", "name"});
-    schema->addLeaf("/example:twoKeyList", "example:number", yang::LeafDataTypes::Int32);
-    schema->addLeaf("/example:twoKeyList", "example:name", yang::LeafDataTypes::String);
+    schema->addLeaf("/example:twoKeyList", "example:number", yang::Int32{});
+    schema->addLeaf("/example:twoKeyList", "example:name", yang::String{});
     auto mockDatastore = std::make_shared<MockDatastoreAccess>();
 
     // DataQuery gets the schema from DatastoreAccess once
diff --git a/tests/leaf_data_helpers.hpp b/tests/leaf_data_helpers.hpp
new file mode 100644
index 0000000..0a28ae3
--- /dev/null
+++ b/tests/leaf_data_helpers.hpp
@@ -0,0 +1,10 @@
+#include <algorithm>
+#include "leaf_data_type.hpp"
+yang::Enum createEnum(const std::initializer_list<const char*>& list)
+{
+    std::set<enum_> enums;
+    std::transform(list.begin(), list.end(), std::inserter(enums, enums.end()), [](const auto& value) {
+        return enum_{value};
+    });
+    return yang::Enum(std::move(enums));
+}
diff --git a/tests/leaf_editing.cpp b/tests/leaf_editing.cpp
index 655f269..ad88a99 100644
--- a/tests/leaf_editing.cpp
+++ b/tests/leaf_editing.cpp
@@ -9,6 +9,7 @@
 #include "trompeloeil_doctest.hpp"
 #include <boost/core/demangle.hpp>
 #include "ast_commands.hpp"
+#include "leaf_data_helpers.hpp"
 #include "parser.hpp"
 #include "static_schema.hpp"
 #include "utils.hpp"
@@ -24,33 +25,53 @@
     schema->addModule("mod");
     schema->addModule("pizza-module");
     schema->addContainer("/", "mod:contA");
-    schema->addLeaf("/", "mod:leafString", yang::LeafDataTypes::String);
-    schema->addLeaf("/", "mod:leafDecimal", yang::LeafDataTypes::Decimal);
-    schema->addLeaf("/", "mod:leafBool", yang::LeafDataTypes::Bool);
-    schema->addLeaf("/", "mod:leafInt8", yang::LeafDataTypes::Int8);
-    schema->addLeaf("/", "mod:leafInt16", yang::LeafDataTypes::Int16);
-    schema->addLeaf("/", "mod:leafInt32", yang::LeafDataTypes::Int32);
-    schema->addLeaf("/", "mod:leafInt64", yang::LeafDataTypes::Int64);
-    schema->addLeaf("/", "mod:leafUint8", yang::LeafDataTypes::Uint8);
-    schema->addLeaf("/", "mod:leafUint16", yang::LeafDataTypes::Uint16);
-    schema->addLeaf("/", "mod:leafUint32", yang::LeafDataTypes::Uint32);
-    schema->addLeaf("/", "mod:leafUint64", yang::LeafDataTypes::Uint64);
-    schema->addLeaf("/", "mod:leafBinary", yang::LeafDataTypes::Binary);
+    schema->addLeaf("/", "mod:leafString", yang::String{});
+    schema->addLeaf("/", "mod:leafDecimal", yang::Decimal{});
+    schema->addLeaf("/", "mod:leafBool", yang::Bool{});
+    schema->addLeaf("/", "mod:leafInt8", yang::Int8{});
+    schema->addLeaf("/", "mod:leafInt16", yang::Int16{});
+    schema->addLeaf("/", "mod:leafInt32", yang::Int32{});
+    schema->addLeaf("/", "mod:leafInt64", yang::Int64{});
+    schema->addLeaf("/", "mod:leafUint8", yang::Uint8{});
+    schema->addLeaf("/", "mod:leafUint16", yang::Uint16{});
+    schema->addLeaf("/", "mod:leafUint32", yang::Uint32{});
+    schema->addLeaf("/", "mod:leafUint64", yang::Uint64{});
+    schema->addLeaf("/", "mod:leafBinary", yang::Binary{});
     schema->addIdentity(std::nullopt, ModuleValuePair{"mod", "food"});
     schema->addIdentity(std::nullopt, ModuleValuePair{"mod", "vehicle"});
     schema->addIdentity(ModuleValuePair{"mod", "food"}, ModuleValuePair{"mod", "pizza"});
     schema->addIdentity(ModuleValuePair{"mod", "food"}, ModuleValuePair{"mod", "spaghetti"});
     schema->addIdentity(ModuleValuePair{"mod", "pizza"}, ModuleValuePair{"pizza-module", "hawaii"});
-    schema->addLeafIdentityRef("/", "mod:foodIdentRef", ModuleValuePair{"mod", "food"});
-    schema->addLeafIdentityRef("/", "mod:pizzaIdentRef", ModuleValuePair{"mod", "pizza"});
-    schema->addLeafIdentityRef("/mod:contA", "mod:identInCont", ModuleValuePair{"mod", "pizza"});
-    schema->addLeafEnum("/", "mod:leafEnum", {"lol", "data", "coze"});
-    schema->addLeaf("/mod:contA", "mod:leafInCont", yang::LeafDataTypes::String);
+    schema->addLeaf("/", "mod:foodIdentRef", yang::IdentityRef{schema->validIdentities("mod", "food")});
+    schema->addLeaf("/", "mod:pizzaIdentRef", yang::IdentityRef{schema->validIdentities("mod", "pizza")});
+    schema->addLeaf("/mod:contA", "mod:identInCont", yang::IdentityRef{schema->validIdentities("mod", "pizza")});
+    schema->addLeaf("/", "mod:leafEnum", createEnum({"lol", "data", "coze"}));
+    schema->addLeaf("/mod:contA", "mod:leafInCont", yang::String{});
     schema->addList("/", "mod:list", {"number"});
-    schema->addLeaf("/mod:list", "mod:number", yang::LeafDataTypes::Int32);
-    schema->addLeaf("/mod:list", "mod:leafInList", yang::LeafDataTypes::String);
-    schema->addLeafRef("/", "mod:refToString", "/mod:leafString");
-    schema->addLeafRef("/", "mod:refToInt8", "/mod:leafInt8");
+    schema->addLeaf("/mod:list", "mod:number", yang::Int32{});
+    schema->addLeaf("/mod:list", "mod:leafInList", yang::String{});
+    schema->addLeaf("/", "mod:refToString", yang::LeafRef{"/mod:leafString", std::make_unique<yang::LeafDataType>(schema->leafType("/mod:leafString"))});
+    schema->addLeaf("/", "mod:refToInt8", yang::LeafRef{"/mod:leafInt8", std::make_unique<yang::LeafDataType>(schema->leafType("/mod:leafInt8"))});
+    schema->addLeaf("/", "mod:refToLeafInCont", yang::LeafRef{"/mod:contA/identInCont", std::make_unique<yang::LeafDataType>(schema->leafType("/mod:contA/mod:identInCont"))});
+    schema->addLeaf("/", "mod:intOrString", yang::Union{{yang::Int32{}, yang::String{}}});
+    schema->addLeaf("/", "mod:twoInts", yang::Union{{yang::Uint8{}, yang::Int16{}}});
+    schema->addLeaf("/", "mod:unionStringEnumLeafref", yang::Union{{
+        yang::String{},
+        createEnum({"foo", "bar"}),
+        yang::LeafRef{"/mod:leafEnum", std::make_unique<yang::LeafDataType>(schema->leafType("/mod:leafEnum"))}
+    }});
+
+    schema->addList("/", "mod:portSettings", {"port"});
+    schema->addLeaf("/mod:portSettings", "mod:port", createEnum({"eth0", "eth1", "eth2"}));
+    schema->addList("/", "mod:portMapping", {"port"});
+    schema->addLeaf("/mod:portMapping", "mod:port", createEnum({"utf1", "utf2", "utf3"}));
+    schema->addLeaf("/", "mod:activeMappedPort", yang::LeafRef{"/mod:portMapping/mod:port", std::make_unique<yang::LeafDataType>(schema->leafType("/mod:portMapping/mod:port"))});
+    schema->addLeaf("/", "mod:activePort", yang::Union{{
+        createEnum({"wlan0", "wlan1"}),
+        yang::LeafRef{"/mod:portSettings/mod:port", std::make_unique<yang::LeafDataType>(schema->leafType("/mod:portSettings/mod:port"))},
+        yang::LeafRef{"/mod:activeMappedPort", std::make_unique<yang::LeafDataType>(schema->leafType("/mod:activeMappedPort"))},
+    }});
+
     Parser parser(schema);
     std::string input;
     std::ostringstream errorStream;
@@ -201,6 +222,112 @@
                 expected.m_data = true;
             }
 
+            SECTION("union")
+            {
+                SECTION("int")
+                {
+                    expected.m_path.m_nodes.push_back(dataNode_{module_{"mod"}, leaf_("intOrString")});
+                    input = "set mod:intOrString 90";
+                    expected.m_data = int32_t{90};
+                }
+                SECTION("string")
+                {
+                    expected.m_path.m_nodes.push_back(dataNode_{module_{"mod"}, leaf_("intOrString")});
+                    input = "set mod:intOrString \"test\"";
+                    expected.m_data = std::string{"test"};
+                }
+
+                SECTION("union with two integral types")
+                {
+                    expected.m_path.m_nodes.push_back(dataNode_{module_{"mod"}, leaf_("twoInts")});
+                    SECTION("uint8")
+                    {
+                        input = "set mod:twoInts 100";
+                        expected.m_data = uint8_t{100};
+                    }
+                    SECTION("int16")
+                    {
+                        input = "set mod:twoInts 6666";
+                        expected.m_data = int16_t{6666};
+                    }
+                }
+
+                SECTION("union with enum and leafref to enum")
+                {
+                    expected.m_path.m_nodes.push_back(dataNode_{module_{"mod"}, leaf_("unionStringEnumLeafref")});
+                    SECTION("string")
+                    {
+                        input = "set mod:unionStringEnumLeafref \"AHOJ\"";
+                        expected.m_data = std::string{"AHOJ"};
+                    }
+                    SECTION("enum")
+                    {
+                        input = "set mod:unionStringEnumLeafref bar";
+                        expected.m_data = enum_("bar");
+                    }
+                    SECTION("enum leafref")
+                    {
+                        input = "set mod:unionStringEnumLeafref coze";
+                        expected.m_data = enum_("coze");
+                    }
+                }
+
+                SECTION("activePort")
+                {
+                    expected.m_path.m_nodes.push_back(dataNode_{module_{"mod"}, leaf_("activePort")});
+                    input = "set mod:activePort ";
+                    SECTION("1. anonymous enum")
+                    {
+                        SECTION("wlan0")
+                        {
+                            input += "wlan0";
+                            expected.m_data = enum_("wlan0");
+                        }
+                        SECTION("wlan1")
+                        {
+                            input += "wlan1";
+                            expected.m_data = enum_("wlan1");
+                        }
+                    }
+                    SECTION("2. leafref to enum")
+                    {
+                        SECTION("eth0")
+                        {
+                            input += "eth0";
+                            expected.m_data = enum_("eth0");
+                        }
+                        SECTION("eth1")
+                        {
+                            input += "eth1";
+                            expected.m_data = enum_("eth1");
+                        }
+                        SECTION("eth2")
+                        {
+                            input += "eth2";
+                            expected.m_data = enum_("eth2");
+                        }
+                    }
+                    SECTION("3. leafref to leafref")
+                    {
+                        SECTION("utf1")
+                        {
+                            input += "utf1";
+                            expected.m_data = enum_("utf1");
+                        }
+                        SECTION("utf2")
+                        {
+                            input += "utf2";
+                            expected.m_data = enum_("utf2");
+                        }
+                        SECTION("utf3")
+                        {
+                            input += "utf3";
+                            expected.m_data = enum_("utf3");
+                        }
+                    }
+                }
+            }
+
             SECTION("binary")
             {
                 SECTION("zero ending '='")
@@ -313,6 +440,13 @@
                     expected.m_path.m_nodes.push_back(dataNode_{module_{"mod"}, leaf_("refToInt8")});
                     expected.m_data = int8_t{42};
                 }
+
+                SECTION("refToLeafInCont")
+                {
+                    input = "set mod:refToLeafInCont pizza";
+                    expected.m_path.m_nodes.push_back(dataNode_{module_{"mod"}, leaf_("refToLeafInCont")});
+                    expected.m_data = identityRef_{"pizza"};
+                }
             }
         }
 
@@ -445,6 +579,11 @@
             input = "set mod:contA/identInCont pizza-module:";
         }
 
+        SECTION("set a union path to a wrong type")
+        {
+            input = "set mod:intOrString true";
+        }
+
         REQUIRE_THROWS_AS(parser.parseCommand(input, errorStream), InvalidCommandException);
         REQUIRE(errorStream.str().find(expectedError) != std::string::npos);
     }
diff --git a/tests/list_manipulation.cpp b/tests/list_manipulation.cpp
index b71ee7b..15f2f04 100644
--- a/tests/list_manipulation.cpp
+++ b/tests/list_manipulation.cpp
@@ -14,8 +14,8 @@
     auto schema = std::make_shared<StaticSchema>();
     schema->addModule("mod");
     schema->addList("/", "mod:list", {"number"});
-    schema->addLeaf("/mod:list", "mod:number", yang::LeafDataTypes::Int32);
-    schema->addLeaf("/mod:list", "mod:leafInList", yang::LeafDataTypes::String);
+    schema->addLeaf("/mod:list", "mod:number", yang::Int32{});
+    schema->addLeaf("/mod:list", "mod:leafInList", yang::String{});
     Parser parser(schema);
     std::string input;
     std::ostringstream errorStream;
diff --git a/tests/ls.cpp b/tests/ls.cpp
index 2c72041..66c2aaa 100644
--- a/tests/ls.cpp
+++ b/tests/ls.cpp
@@ -25,11 +25,11 @@
     schema->addContainer("/example:a/example:a2", "example:a3");
     schema->addContainer("/example:b/example:b2", "example:b3");
     schema->addList("/", "example:list", {"number"});
-    schema->addLeaf("/example:list", "example:number", yang::LeafDataTypes::Int32);
+    schema->addLeaf("/example:list", "example:number", yang::Int32{});
     schema->addContainer("/example:list", "example:contInList");
     schema->addList("/", "example:twoKeyList", {"number", "name"});
-    schema->addLeaf("/example:twoKeyList", "example:number", yang::LeafDataTypes::Int32);
-    schema->addLeaf("/example:twoKeyList", "example:name", yang::LeafDataTypes::String);
+    schema->addLeaf("/example:twoKeyList", "example:number", yang::Int32{});
+    schema->addLeaf("/example:twoKeyList", "example:name", yang::String{});
     Parser parser(schema);
     std::string input;
     std::ostringstream errorStream;
diff --git a/tests/path_completion.cpp b/tests/path_completion.cpp
index 2403f9d..c00a26c 100644
--- a/tests/path_completion.cpp
+++ b/tests/path_completion.cpp
@@ -27,16 +27,16 @@
     schema->addContainer("/example:ano/example:a2", "example:a3");
     schema->addContainer("/example:bota/example:b2", "example:b3");
     schema->addList("/", "example:list", {"number"});
-    schema->addLeaf("/example:list", "example:number", yang::LeafDataTypes::Int32);
+    schema->addLeaf("/example:list", "example:number", yang::Int32{});
     schema->addContainer("/example:list", "example:contInList");
     schema->addList("/", "example:ovoce", {"name"});
-    schema->addLeaf("/example:ovoce", "example:name", yang::LeafDataTypes::String);
+    schema->addLeaf("/example:ovoce", "example:name", yang::String{});
     schema->addList("/", "example:ovocezelenina", {"name"});
-    schema->addLeaf("/example:ovocezelenina", "example:name", yang::LeafDataTypes::String);
+    schema->addLeaf("/example:ovocezelenina", "example:name", yang::String{});
     schema->addList("/", "example:twoKeyList", {"number", "name"});
-    schema->addLeaf("/example:twoKeyList", "example:name", yang::LeafDataTypes::String);
-    schema->addLeaf("/example:twoKeyList", "example:number", yang::LeafDataTypes::Int32);
-    schema->addLeaf("/", "example:leafInt", yang::LeafDataTypes::Int32);
+    schema->addLeaf("/example:twoKeyList", "example:name", yang::String{});
+    schema->addLeaf("/example:twoKeyList", "example:number", yang::Int32{});
+    schema->addLeaf("/", "example:leafInt", yang::Int32{});
     auto mockDatastore = std::make_shared<MockDatastoreAccess>();
 
     // The parser will use DataQuery for key value completion, but I'm not testing that here, so I don't return anything.
diff --git a/tests/presence_containers.cpp b/tests/presence_containers.cpp
index af79d00..7c6397d 100644
--- a/tests/presence_containers.cpp
+++ b/tests/presence_containers.cpp
@@ -22,7 +22,7 @@
     schema->addContainer("/mod:a/mod:a2", "mod:a3", yang::ContainerTraits::Presence);
     schema->addContainer("/mod:b", "mod:b2", yang::ContainerTraits::Presence);
     schema->addList("/", "mod:list", {"quote"});
-    schema->addLeaf("/mod:list", "mod:quote", yang::LeafDataTypes::String);
+    schema->addLeaf("/mod:list", "mod:quote", yang::String{});
     schema->addContainer("/mod:list", "mod:contInList", yang::ContainerTraits::Presence);
     Parser parser(schema);
     std::string input;
diff --git a/tests/pretty_printers.hpp b/tests/pretty_printers.hpp
index 86bd20d..e5b6659 100644
--- a/tests/pretty_printers.hpp
+++ b/tests/pretty_printers.hpp
@@ -36,4 +36,42 @@
     s << "}" << std::endl;
     return s;
 }
+
+std::ostream& operator<<(std::ostream& s, const yang::LeafDataType& type)
+{
+    s << std::endl
+      << leafDataTypeToString(type);
+    if (std::holds_alternative<yang::Enum>(type)) {
+        s << "{";
+        auto values = std::get<yang::Enum>(type).m_allowedValues;
+        std::transform(values.begin(), values.end(), std::experimental::make_ostream_joiner(s, ", "), [](const auto& value) {
+            return value.m_value;
+        });
+        s << "}";
+    }
+    if (std::holds_alternative<yang::IdentityRef>(type)) {
+        s << "{";
+        auto values = std::get<yang::IdentityRef>(type).m_allowedValues;
+        std::transform(values.begin(), values.end(), std::experimental::make_ostream_joiner(s, ", "), [](const auto& value) {
+            std::string res;
+            if (value.m_prefix) {
+                res += value.m_prefix->m_name;
+                res += ":";
+            }
+            res += value.m_value;
+            return res;
+        });
+        s << "}";
+    }
+    if (std::holds_alternative<yang::LeafRef>(type)) {
+        s << "{" << std::get<yang::LeafRef>(type).m_targetXPath << "," << *std::get<yang::LeafRef>(type).m_targetType  << "}";
+    }
+    if (std::holds_alternative<yang::Union>(type)) {
+        s << "{" << std::endl;
+        auto types = std::get<yang::Union>(type).m_unionTypes;
+        std::copy(types.begin(), types.end(), std::experimental::make_ostream_joiner(s, ",\n"));
+    }
+    s << std::endl;
+    return s;
+}
 }
diff --git a/tests/utils.cpp b/tests/utils.cpp
index 91c61cb..cea698b 100644
--- a/tests/utils.cpp
+++ b/tests/utils.cpp
@@ -8,6 +8,7 @@
 
 #include "trompeloeil_doctest.hpp"
 #include "completion.hpp"
+#include "leaf_data_helpers.hpp"
 #include "utils.hpp"
 
 TEST_CASE("utils")
@@ -80,4 +81,24 @@
 
         REQUIRE(joinPaths(prefix, suffix) == result);
     }
+
+    SECTION("leafDataTypeToString")
+    {
+        yang::LeafDataType type;
+        std::string expected;
+        SECTION("union")
+        {
+            type = yang::Union{{
+                yang::String{},
+                createEnum({"foo", "bar"}),
+                yang::Int8{},
+                yang::Int64{},
+            }};
+            expected = "a string, an enum, an 8-bit integer, a 64-bit integer";
+        }
+
+        REQUIRE(leafDataTypeToString(type) == expected);
+
+    }
+
 }
diff --git a/tests/yang.cpp b/tests/yang.cpp
index d189f50..a9fb65e 100644
--- a/tests/yang.cpp
+++ b/tests/yang.cpp
@@ -7,6 +7,7 @@
 */
 
 #include <experimental/iterator>
+#include "leaf_data_helpers.hpp"
 #include "pretty_printers.hpp"
 #include "trompeloeil_doctest.hpp"
 #include "yang_schema.hpp"
@@ -298,6 +299,61 @@
 
     rpc myRpc {}
 
+    leaf numberOrString {
+        type union {
+            type int32;
+            type string;
+        }
+        description "Can be an int32 or a string.";
+    }
+
+    list portSettings {
+        key "port";
+        leaf port {
+            type enumeration {
+                enum eth0;
+                enum eth1;
+                enum eth2;
+            }
+        }
+    }
+
+    feature weirdPortNames;
+
+    list portMapping {
+        key "port";
+        leaf port {
+            type enumeration {
+                enum WEIRD {
+                    if-feature "weirdPortNames";
+                }
+                enum utf2;
+                enum utf3;
+            }
+        }
+    }
+
+    leaf activeMappedPort {
+        type leafref {
+            path "../portMapping/port";
+        }
+    }
+
+    leaf activePort {
+        type union {
+            type enumeration {
+                enum wlan0;
+                enum wlan1;
+            }
+            type leafref {
+                path "../portSettings/port";
+            }
+            type leafref {
+                path "../activeMappedPort";
+            }
+        }
+    }
+
 })";
 
 namespace std {
@@ -425,189 +481,6 @@
 
             REQUIRE(ys.isPresenceContainer(path, node));
         }
-        SECTION("leafEnumHasValue")
-        {
-            std::string value;
-            SECTION("leafEnum")
-            {
-                node.first = "example-schema";
-                node.second = "leafEnum";
-
-                SECTION("lol")
-                    value = "lol";
-
-                SECTION("data")
-                    value = "data";
-
-                SECTION("coze")
-                    value = "coze";
-            }
-
-            SECTION("leafEnumTypedef")
-            {
-                node.first = "example-schema";
-                node.second = "leafEnumTypedef";
-
-                SECTION("lol")
-                    value = "lol";
-
-                SECTION("data")
-                    value = "data";
-
-                SECTION("coze")
-                    value = "coze";
-            }
-
-            SECTION("leafEnumTypedefRestricted")
-            {
-                node.first = "example-schema";
-                node.second = "leafEnumTypedefRestricted";
-
-                SECTION("data")
-                    value = "data";
-
-                SECTION("coze")
-                    value = "coze";
-            }
-
-            SECTION("leafEnumTypedefRestricted2")
-            {
-                node.first = "example-schema";
-                node.second = "leafEnumTypedefRestricted2";
-
-                SECTION("lol")
-                    value = "lol";
-
-                SECTION("data")
-                    value = "data";
-            }
-
-            SECTION("pizzaSize")
-            {
-                node.first = "example-schema";
-                node.second = "pizzaSize";
-
-                SECTION("small")
-                {
-                    value = "small";
-                }
-                SECTION("medium")
-                {
-                    value = "medium";
-                }
-
-                SECTION("large")
-                {
-                    ys.enableFeature("example-schema", "bigPizzas");
-                    value = "large";
-                }
-            }
-
-            REQUIRE(ys.leafEnumHasValue(path, node, value));
-        }
-        SECTION("leafIdentityIsValid")
-        {
-            ModuleValuePair value;
-
-            SECTION("foodIdentLeaf")
-            {
-                node.first = "example-schema";
-                node.second = "foodIdentLeaf";
-
-                SECTION("food")
-                {
-                    value.second = "food";
-                }
-                SECTION("example-schema:food")
-                {
-                    value.first = "example-schema";
-                    value.second = "food";
-                }
-                SECTION("pizza")
-                {
-                    value.second = "pizza";
-                }
-                SECTION("example-schema:pizza")
-                {
-                    value.first = "example-schema";
-                    value.second = "pizza";
-                }
-                SECTION("hawaii")
-                {
-                    value.second = "hawaii";
-                }
-                SECTION("example-schema:hawaii")
-                {
-                    value.first = "example-schema";
-                    value.second = "hawaii";
-                }
-                SECTION("fruit")
-                {
-                    value.second = "fruit";
-                }
-                SECTION("example-schema:fruit")
-                {
-                    value.first = "example-schema";
-                    value.second = "fruit";
-                }
-                SECTION("second-schema:pineapple")
-                {
-                    value.first = "second-schema";
-                    value.second = "pineapple";
-                }
-            }
-
-            SECTION("pizzaIdentLeaf")
-            {
-                node.first = "example-schema";
-                node.second = "pizzaIdentLeaf";
-
-                SECTION("pizza")
-                {
-                    value.second = "pizza";
-                }
-                SECTION("example-schema:pizza")
-                {
-                    value.first = "example-schema";
-                    value.second = "pizza";
-                }
-                SECTION("hawaii")
-                {
-                    value.second = "hawaii";
-                }
-                SECTION("example-schema:hawaii")
-                {
-                    value.first = "example-schema";
-                    value.second = "hawaii";
-                }
-            }
-
-            SECTION("foodDrinkIdentLeaf")
-            {
-                node.first = "example-schema";
-                node.second = "foodDrinkIdentLeaf";
-
-                SECTION("food")
-                {
-                    value.second = "food";
-                }
-                SECTION("example-schema:food")
-                {
-                    value.first = "example-schema";
-                    value.second = "food";
-                }
-                SECTION("drink")
-                {
-                    value.second = "drink";
-                }
-                SECTION("example-schema:drink")
-                {
-                    value.first = "example-schema";
-                    value.second = "drink";
-                }
-            }
-            REQUIRE(ys.leafIdentityIsValid(path, node, value));
-        }
 
         SECTION("listHasKey")
         {
@@ -655,90 +528,208 @@
         }
         SECTION("leafType")
         {
-            yang::LeafDataTypes type;
+            yang::LeafDataType type;
 
             SECTION("leafString")
             {
                 node.first = "example-schema";
                 node.second = "leafString";
-                type = yang::LeafDataTypes::String;
+                type = yang::String{};
             }
 
             SECTION("leafDecimal")
             {
                 node.first = "example-schema";
                 node.second = "leafDecimal";
-                type = yang::LeafDataTypes::Decimal;
+                type = yang::Decimal{};
             }
 
             SECTION("leafBool")
             {
                 node.first = "example-schema";
                 node.second = "leafBool";
-                type = yang::LeafDataTypes::Bool;
+                type = yang::Bool{};
             }
 
             SECTION("leafInt8")
             {
                 node.first = "example-schema";
                 node.second = "leafInt8";
-                type = yang::LeafDataTypes::Int8;
+                type = yang::Int8{};
             }
 
             SECTION("leafUint8")
             {
                 node.first = "example-schema";
                 node.second = "leafUint8";
-                type = yang::LeafDataTypes::Uint8;
+                type = yang::Uint8{};
             }
 
-            SECTION("leafInt15")
+            SECTION("leafInt16")
             {
                 node.first = "example-schema";
                 node.second = "leafInt16";
-                type = yang::LeafDataTypes::Int16;
+                type = yang::Int16{};
             }
 
             SECTION("leafUint16")
             {
                 node.first = "example-schema";
                 node.second = "leafUint16";
-                type = yang::LeafDataTypes::Uint16;
+                type = yang::Uint16{};
             }
 
             SECTION("leafInt32")
             {
                 node.first = "example-schema";
                 node.second = "leafInt32";
-                type = yang::LeafDataTypes::Int32;
+                type = yang::Int32{};
             }
 
             SECTION("leafUint32")
             {
                 node.first = "example-schema";
                 node.second = "leafUint32";
-                type = yang::LeafDataTypes::Uint32;
+                type = yang::Uint32{};
             }
 
             SECTION("leafInt64")
             {
                 node.first = "example-schema";
                 node.second = "leafInt64";
-                type = yang::LeafDataTypes::Int64;
+                type = yang::Int64{};
             }
 
             SECTION("leafUint64")
             {
                 node.first = "example-schema";
                 node.second = "leafUint64";
-                type = yang::LeafDataTypes::Uint64;
+                type = yang::Uint64{};
             }
 
             SECTION("leafEnum")
             {
                 node.first = "example-schema";
                 node.second = "leafEnum";
-                type = yang::LeafDataTypes::Enum;
+                type = createEnum({"lol", "data", "coze"});
+            }
+
+            SECTION("leafEnumTypedef")
+            {
+                node.first = "example-schema";
+                node.second = "leafEnumTypedef";
+                type = createEnum({"lol", "data", "coze"});
+            }
+
+            SECTION("leafEnumTypedefRestricted")
+            {
+                node.first = "example-schema";
+                node.second = "leafEnumTypedefRestricted";
+                type = createEnum({"data", "coze"});
+            }
+
+            SECTION("leafEnumTypedefRestricted2")
+            {
+                node.first = "example-schema";
+                node.second = "leafEnumTypedefRestricted2";
+                type = createEnum({"lol", "data"});
+            }
+
+            SECTION("pizzaSize")
+            {
+                node.first = "example-schema";
+                node.second = "pizzaSize";
+
+                SECTION("bigPizzas disabled")
+                {
+                    type = createEnum({"small", "medium"});
+                }
+                SECTION("bigPizzas enabled")
+                {
+                    ys.enableFeature("example-schema", "bigPizzas");
+                    type = createEnum({"small", "medium", "large"});
+                }
+            }
+
+            SECTION("foodIdentLeaf")
+            {
+                node.first = "example-schema";
+                node.second = "foodIdentLeaf";
+                type = yang::IdentityRef{{{"second-schema", "pineapple"},
+                                          {"example-schema", "food"},
+                                          {"example-schema", "pizza"},
+                                          {"example-schema", "hawaii"},
+                                          {"example-schema", "fruit"}}};
+            }
+
+            SECTION("pizzaIdentLeaf")
+            {
+                node.first = "example-schema";
+                node.second = "pizzaIdentLeaf";
+
+                type = yang::IdentityRef{{
+                    {"example-schema", "pizza"},
+                    {"example-schema", "hawaii"},
+                }};
+            }
+
+            SECTION("foodDrinkIdentLeaf")
+            {
+                node.first = "example-schema";
+                node.second = "foodDrinkIdentLeaf";
+
+                type = yang::IdentityRef{{
+                    {"example-schema", "food"},
+                    {"example-schema", "drink"},
+                    {"example-schema", "fruit"},
+                    {"example-schema", "hawaii"},
+                    {"example-schema", "pizza"},
+                    {"example-schema", "voda"},
+                    {"second-schema", "pineapple"},
+                }};
+            }
+
+            SECTION("activeNumber")
+            {
+                node.first = "example-schema";
+                node.second = "activeNumber";
+                type.emplace<yang::LeafRef>(
+                    "/example-schema:_list/number",
+                    std::make_unique<yang::LeafDataType>(ys.leafType("/example-schema:_list/number"))
+                );
+            }
+
+            SECTION("activePort")
+            {
+                node.first = "example-schema";
+                node.second = "activePort";
+
+                yang::Enum enums = [&ys]() {
+                    SECTION("weird ports disabled")
+                    {
+                        return createEnum({"utf2", "utf3"});
+                    }
+                    SECTION("weird ports enabled")
+                    {
+                        ys.enableFeature("example-schema", "weirdPortNames");
+                        return createEnum({"WEIRD", "utf2", "utf3"});
+                    }
+                    __builtin_unreachable();
+                }();
+
+                type = yang::Union{{
+                    createEnum({"wlan0", "wlan1"}),
+                    yang::LeafRef{
+                        "/example-schema:portSettings/port",
+                        std::make_unique<yang::LeafDataType>(createEnum({"eth0", "eth1", "eth2"}))
+                    },
+                    yang::LeafRef{
+                        "/example-schema:activeMappedPort",
+                        std::make_unique<yang::LeafDataType>(yang::LeafRef{
+                                "/example-schema:portMapping/port",
+                                std::make_unique<yang::LeafDataType>(enums)
+                        })},
+                }};
             }
 
             REQUIRE(ys.leafType(path, node) == type);
@@ -765,7 +756,12 @@
                        "example-schema:pizzaSize",
                        "example-schema:length", "example-schema:wavelength",
                        "example-schema:duration", "example-schema:another-duration",
-                       "example-schema:activeNumber"};
+                       "example-schema:activeNumber",
+                       "example-schema:numberOrString",
+                       "example-schema:portSettings",
+                       "example-schema:portMapping",
+                       "example-schema:activeMappedPort",
+                       "example-schema:activePort"};
             }
 
             SECTION("example-schema:a")
@@ -834,6 +830,12 @@
                 path.m_nodes.push_back(schemaNode_(module_{"example-schema"}, leaf_("leafString")));
             }
 
+            SECTION("numberOrString")
+            {
+                path.m_nodes.push_back(schemaNode_(module_{"example-schema"}, leaf_("numberOrString")));
+                expected = "Can be an int32 or a string.";
+            }
+
             REQUIRE(ys.description(pathToSchemaString(path, Prefixes::WhenNeeded)) == expected);
         }
 
@@ -962,7 +964,6 @@
             path.m_nodes.push_back(schemaNode_(module_{"example-schema"}, container_("a")));
             node.second = "a2";
 
-            REQUIRE(!ys.leafEnumHasValue(path, node, "haha"));
             REQUIRE(!ys.listHasKey(path, node, "chacha"));
         }
 
@@ -971,44 +972,6 @@
             REQUIRE(!ys.isModule("notAModule"));
         }
 
-        SECTION("leafIdentityIsValid")
-        {
-            ModuleValuePair value;
-            SECTION("pizzaIdentLeaf")
-            {
-                node.first = "example-schema";
-                node.second = "pizzaIdentLeaf";
-
-                SECTION("wrong base ident")
-                {
-                    SECTION("food")
-                    {
-                        value.second = "food";
-                    }
-                    SECTION("fruit")
-                    {
-                        value.second = "fruit";
-                    }
-                }
-                SECTION("non-existent identity")
-                {
-                    value.second = "nonexistent";
-                }
-                SECTION("weird module")
-                {
-                    value.first = "ahahaha";
-                    value.second = "pizza";
-                }
-            }
-            SECTION("different module identity, but withotu prefix")
-            {
-                node.first = "example-schema";
-                node.second = "foodIdentLeaf";
-                value.second = "pineapple";
-            }
-            REQUIRE_FALSE(ys.leafIdentityIsValid(path, node, value));
-        }
-
         SECTION("grouping is not a node")
         {
             SECTION("example-schema:arithmeticFlags")
@@ -1062,15 +1025,5 @@
             REQUIRE(!ys.isLeaf(path, node));
             REQUIRE(!ys.isContainer(path, node));
         }
-
-        SECTION("enum is disabled by if-feature if feature is not enabled")
-        {
-            node.first = "example-schema";
-            node.second = "pizzaSize";
-
-            std::string value = "large";
-
-            REQUIRE(!ys.leafEnumHasValue(path, node, value));
-        }
     }
 }