Add special type for representing special values

Before this, pseudo-values like lists and containers were represented by
a string leaf value. Now, I can tell these values apart. This can be
helpful for filtering (for example, when printing the values).  It will
also be used for implementing key value completion.

Change-Id: I48f6fa2facaf3e8e52aa3b3b4d92130e63acdb6f
diff --git a/src/ast_values.cpp b/src/ast_values.cpp
index b8ca68d..8d9c40c 100644
--- a/src/ast_values.cpp
+++ b/src/ast_values.cpp
@@ -48,3 +48,21 @@
     return this->m_value == b.m_value;
 }
 
+bool special_::operator==(const special_& b) const
+{
+    return this->m_value == b.m_value;
+}
+
+std::string specialValueToString(const special_& value)
+{
+    switch (value.m_value) {
+    case SpecialValue::Container:
+        return "(container)";
+    case SpecialValue::PresenceContainer:
+        return "(presence container)";
+    case SpecialValue::List:
+        return "(list)";
+    }
+
+    __builtin_unreachable();
+}
diff --git a/src/ast_values.hpp b/src/ast_values.hpp
index 44e98af..b2f2c3a 100644
--- a/src/ast_values.hpp
+++ b/src/ast_values.hpp
@@ -38,9 +38,23 @@
     std::string m_value;
 };
 
+enum class SpecialValue {
+    List,
+    Container,
+    PresenceContainer
+};
+
+struct special_ {
+    bool operator==(const special_& b) const;
+    SpecialValue m_value;
+};
+
+std::string specialValueToString(const special_& value);
+
 using leaf_data_ = boost::variant<enum_,
                                   binary_,
                                   identityRef_,
+                                  special_,
                                   double,
                                   bool,
                                   int8_t,
diff --git a/src/netconf_access.cpp b/src/netconf_access.cpp
index d1619cb..78d80b1 100644
--- a/src/netconf_access.cpp
+++ b/src/netconf_access.cpp
@@ -58,7 +58,7 @@
             if (!it)
                 continue;
             if (it->schema()->nodetype() == LYS_LIST) {
-                res.emplace(it->path(), std::string{"(list)"});
+                res.emplace(it->path(), special_{SpecialValue::List});
             }
             if (it->schema()->nodetype() == LYS_LEAF) {
                 libyang::Data_Node_Leaf_List leaf(it);
diff --git a/src/sysrepo_access.cpp b/src/sysrepo_access.cpp
index b01b484..221f482 100644
--- a/src/sysrepo_access.cpp
+++ b/src/sysrepo_access.cpp
@@ -39,11 +39,11 @@
     case SR_DECIMAL64_T:
         return value->data()->get_decimal64();
     case SR_CONTAINER_T:
-        return "(container)"s;
+        return special_{SpecialValue::Container};
     case SR_CONTAINER_PRESENCE_T:
-        return "(presence container)"s;
+        return special_{SpecialValue::PresenceContainer};
     case SR_LIST_T:
-        return "(list)"s;
+        return special_{SpecialValue::List};
     default: // TODO: implement all types
         return value->val_to_string();
     }
@@ -66,6 +66,11 @@
         return std::make_shared<sysrepo::Val>(res.c_str(), SR_IDENTITYREF_T);
     }
 
+    sysrepo::S_Val operator()(const special_& value) const
+    {
+        throw std::runtime_error("Tried constructing S_Val from a " + specialValueToString(value));
+    }
+
     sysrepo::S_Val operator()(const std::string& value) const
     {
         return std::make_shared<sysrepo::Val>(value.c_str());
diff --git a/src/utils.cpp b/src/utils.cpp
index 672cd36..eb4f432 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -108,6 +108,11 @@
         return data.m_prefix.value().m_name + ":" + data.m_value;
     }
 
+    std::string operator()(const special_& data) const
+    {
+        return specialValueToString(data);
+    }
+
     std::string operator()(const std::string& data) const
     {
         return data;
diff --git a/tests/datastore_access.cpp b/tests/datastore_access.cpp
index 09801b2..535abdc 100644
--- a/tests/datastore_access.cpp
+++ b/tests/datastore_access.cpp
@@ -207,7 +207,7 @@
         // because it'll use the same data structure as libnetconf, so the
         // results will be consistent.
 #ifdef sysrepo_BACKEND
-                                                   {"/example-schema:lol", std::string{"(container)"}},
+                                                   {"/example-schema:lol", special_{SpecialValue::Container}},
 #endif
                                                    {"/example-schema:up", bool{true}}};
         REQUIRE(datastore.getItems("/example-schema:*") == expected);
@@ -240,11 +240,11 @@
             datastore.commitChanges();
         }
         std::map<std::string, leaf_data_> expected{
-            {"/example-schema:person[name='Jan']", std::string{"(list)"}},
+            {"/example-schema:person[name='Jan']", special_{SpecialValue::List}},
             {"/example-schema:person[name='Jan']/name", std::string{"Jan"}},
-            {"/example-schema:person[name='Michal']", std::string{"(list)"}},
+            {"/example-schema:person[name='Michal']", special_{SpecialValue::List}},
             {"/example-schema:person[name='Michal']/name", std::string{"Michal"}},
-            {"/example-schema:person[name='Petr']", std::string{"(list)"}},
+            {"/example-schema:person[name='Petr']", special_{SpecialValue::List}},
             {"/example-schema:person[name='Petr']/name", std::string{"Petr"}}
         };