Rework yang::LeafDataType

Before this patch, Schema::leafType was used like this: you called the
method, and it would give you an enum value from which you could tell
the type. In case the type was, for example, yang::LeafDataTypes::Enum,
you would call enumValues to get the valid values from the enum.
However, this approach came up as a problem when implementing the union
yang type. Example: if the union consisted of two different enums, there
was no way to differentiate those two enums (so enumValues wouldn't know
which one to return). The new approach encodes everything inside the
return value of Schema::leafType, so no futher method calls are needed.
This also means, that methods like Schema::leafEnumIsValid and others
are unneeded, since you get all the info from Schema::leafType.

Also, I got rid of all the on_success handlers, and moved the code to
inside to impl_LeafData. This means that enum/identityref validity is
checked inside there. If I wanted to keep the on_success handlers I
would have create another member in ParserContext (which would contain
the values), but it doesn't give any advantage, except that it
automatically rolls back the iterator on fail (I have to do that myself
now, but I don't think it's a huge deal).
createSetSuggestions::on_success was also moved to impl_LeafData.

Change-Id: Ie9a30174094b73f2c25af8cfc88d0aa5e9e882b3
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..7139fc4 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,124 @@
 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);
+    }
+};
 
 struct LeafData : x3::parser<LeafData> {
     using attribute_type = leaf_data_;
@@ -141,43 +186,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 +206,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..815d0aa
--- /dev/null
+++ b/src/leaf_data_type.cpp
@@ -0,0 +1,90 @@
+/*
+ * 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 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..66f4522
--- /dev/null
+++ b/src/leaf_data_type.hpp
@@ -0,0 +1,89 @@
+/*
+ * 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>
+
+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;
+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
+>;
+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;
+};
+}
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..4964e8e 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,41 +113,6 @@
     }
 }
 
-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;
@@ -189,34 +130,17 @@
     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 +215,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..719f28b 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -52,42 +52,72 @@
     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 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..0f1a3fd 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,102 @@
     return keys;
 }
 
-yang::LeafDataTypes lyTypeToLeafDataTypes(LY_DATA_TYPE type)
+namespace {
+std::set<enum_> enumValues(const libyang::S_Schema_Node_Leaf& leaf)
 {
-    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 = 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<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_Schema_Node_Leaf& leaf)
+{
+    std::set<identityRef_> identSet;
+
+    // auto topLevelModule = leaf->module();
+
+    auto info = leaf->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_Schema_Node_Leaf& leaf)
+{
+    return leaf->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);
+    auto baseType{leaf->type()->base()};
+    switch (baseType) {
+    case LY_TYPE_STRING:
+        return yang::String{};
+    case LY_TYPE_DEC64:
+        return yang::Decimal{};
+    case LY_TYPE_BOOL:
+        return yang::Bool{};
+    case LY_TYPE_INT8:
+        return yang::Int8{};
+    case LY_TYPE_INT16:
+        return yang::Int16{};
+    case LY_TYPE_INT32:
+        return yang::Int32{};
+    case LY_TYPE_INT64:
+        return yang::Int64{};
+    case LY_TYPE_UINT8:
+        return yang::Uint8{};
+    case LY_TYPE_UINT16:
+        return yang::Uint16{};
+    case LY_TYPE_UINT32:
+        return yang::Uint32{};
+    case LY_TYPE_UINT64:
+        return yang::Uint64{};
+    case LY_TYPE_BINARY:
+        return yang::Binary{};
+    case LY_TYPE_ENUM:
+        return yang::Enum{enumValues(leaf)};
+    case LY_TYPE_IDENT:
+        return yang::IdentityRef{validIdentities(leaf)};
+    case LY_TYPE_LEAFREF:
+        return yang::LeafRef{::leafrefPath(leaf), std::make_unique<yang::LeafDataType>(leafType(::leafrefPath(leaf)))};
+    default:
+        using namespace std::string_literals;
+        throw UnsupportedYangTypeException("the type of "s + node->name() + " is not supported: " + std::to_string(baseType));
     }
 }
-}
 
-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 +259,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/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..ca720c5 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,33 @@
     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"))});
     Parser parser(schema);
     std::string input;
     std::ostringstream errorStream;
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..3f592a5 100644
--- a/tests/pretty_printers.hpp
+++ b/tests/pretty_printers.hpp
@@ -36,4 +36,34 @@
     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 << "}";
+    }
+    s << std::endl;
+    return s;
+}
 }
diff --git a/tests/yang.cpp b/tests/yang.cpp
index fc57d3f..dcf4354 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"
@@ -425,189 +426,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,97 +473,175 @@
         }
         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("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 = yang::LeafDataTypes::LeafRef;
+                type.emplace<yang::LeafRef>(
+                    "/example-schema:_list/number",
+                    std::make_unique<yang::LeafDataType>(ys.leafType("/example-schema:_list/number"))
+                );
             }
 
             REQUIRE(ys.leafType(path, node) == type);
@@ -969,7 +865,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"));
         }
 
@@ -978,44 +873,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")
@@ -1069,15 +926,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));
-        }
     }
 }