Add datastore support for bits

Change-Id: I9a1619f6b892bbde71e75376e9a3a84911236b5d
diff --git a/src/ast_values.cpp b/src/ast_values.cpp
index c3f2ce7..da65d7c 100644
--- a/src/ast_values.cpp
+++ b/src/ast_values.cpp
@@ -35,6 +35,16 @@
 
 empty_::empty_() = default;
 
+bool bits_::operator==(const bits_& other) const
+{
+    return this->m_bits == other.m_bits;
+}
+
+bool bits_::operator<(const bits_& other) const
+{
+    return this->m_bits < other.m_bits;
+}
+
 bool module_::operator<(const module_& b) const
 {
     return this->m_name < b.m_name;
diff --git a/src/ast_values.hpp b/src/ast_values.hpp
index f790148..228aac3 100644
--- a/src/ast_values.hpp
+++ b/src/ast_values.hpp
@@ -9,6 +9,7 @@
 
 #include <boost/optional.hpp>
 #include <boost/variant.hpp>
+#include <set>
 
 struct enum_ {
     enum_();
@@ -32,6 +33,12 @@
     bool operator<(const empty_) const;
 };
 
+struct bits_ {
+    bool operator==(const bits_&) const;
+    bool operator<(const bits_&) const;
+    std::set<std::string> m_bits;
+};
+
 struct module_ {
     bool operator==(const module_& b) const;
     bool operator<(const module_& b) const;
@@ -66,6 +73,7 @@
 using leaf_data_ = boost::variant<enum_,
                                   binary_,
                                   empty_,
+                                  bits_,
                                   identityRef_,
                                   special_,
                                   double,
diff --git a/src/libyang_utils.cpp b/src/libyang_utils.cpp
index 456fa62..840d413 100644
--- a/src/libyang_utils.cpp
+++ b/src/libyang_utils.cpp
@@ -36,6 +36,15 @@
         return binary_{value->binary()};
     case LY_TYPE_EMPTY:
         return empty_{};
+    case LY_TYPE_BITS:
+    {
+        auto bits = value->bit();
+        std::vector<libyang::S_Type_Bit> filterNull;
+        std::copy_if(bits.begin(), bits.end(), std::back_inserter(filterNull), [] (auto bit) { return bit; });
+        bits_ res;
+        std::transform(filterNull.begin(), filterNull.end(), std::inserter(res.m_bits, res.m_bits.end()), [] (const auto& bit) { return bit->name(); });
+        return bits_{res};
+    }
     case LY_TYPE_DEC64:
     {
         auto v = value->dec64();
diff --git a/src/sysrepo_access.cpp b/src/sysrepo_access.cpp
index e792ed8..205db3a 100644
--- a/src/sysrepo_access.cpp
+++ b/src/sysrepo_access.cpp
@@ -6,9 +6,11 @@
  *
 */
 
+#include <experimental/iterator>
 #include <libyang/Tree_Data.hpp>
 #include <libyang/Tree_Schema.hpp>
 #include <sysrepo-cpp/Session.hpp>
+#include <sstream>
 #include "libyang_utils.hpp"
 #include "sysrepo_access.hpp"
 #include "utils.hpp"
@@ -57,6 +59,18 @@
         return special_{SpecialValue::PresenceContainer};
     case SR_LIST_T:
         return special_{SpecialValue::List};
+    case SR_BITS_T:
+    {
+        bits_ res;
+        std::istringstream ss(value->data()->get_bits());
+        while (!ss.eof()) {
+            std::string bit;
+            ss >> bit;
+            res.m_bits.insert(bit);
+        }
+        return res;
+
+    }
     default: // TODO: implement all types
         return value->val_to_string();
     }
@@ -94,6 +108,13 @@
         return std::make_shared<sysrepo::Val>(value.c_str());
     }
 
+    sysrepo::S_Val operator()(const bits_& value) const
+    {
+        std::stringstream ss;
+        std::copy(value.m_bits.begin(), value.m_bits.end(), std::experimental::make_ostream_joiner(ss, " "));
+        return std::make_shared<sysrepo::Val>(ss.str().c_str(), SR_BITS_T);
+    }
+
     template <typename T>
     sysrepo::S_Val operator()(const T& value) const
     {
@@ -144,6 +165,13 @@
         }
     }
 
+    auto operator()(const bits_& value) const
+    {
+        std::stringstream ss;
+        std::copy(value.m_bits.begin(), value.m_bits.end(), std::experimental::make_ostream_joiner(ss, " "));
+        v->set(xpath.c_str(), ss.str().c_str(), SR_BITS_T);
+    }
+
     void operator()(const std::string& value) const
     {
         v->set(xpath.c_str(), value.c_str(), SR_STRING_T);
diff --git a/src/utils.cpp b/src/utils.cpp
index b3d3601..11a97b4 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -196,6 +196,13 @@
         }
     }
 
+    std::string operator()(const bits_& data) const
+    {
+        std::stringstream ss;
+        std::copy(data.m_bits.begin(), data.m_bits.end(), std::experimental::make_ostream_joiner(ss, " "));
+        return ss.str();
+    }
+
     template <typename T>
     std::string operator()(const T& data) const
     {
diff --git a/tests/datastore_access.cpp b/tests/datastore_access.cpp
index da3bc48..022b115 100644
--- a/tests/datastore_access.cpp
+++ b/tests/datastore_access.cpp
@@ -508,6 +508,17 @@
         REQUIRE(datastore.getItems("/example-schema:dummy") == expected);
     }
 
+    SECTION("bits")
+    {
+        datastore.setLeaf("/example-schema:flags", bits_{{"sign", "carry"}});
+        REQUIRE_CALL(mock, write("/example-schema:flags", std::nullopt, "carry sign"s));
+        datastore.commitChanges();
+        DatastoreAccess::Tree expected {
+            {"/example-schema:flags", bits_{{"carry", "sign"}}},
+        };
+        REQUIRE(datastore.getItems("/example-schema:flags") == expected);
+    }
+
 #if not defined(yang_BACKEND)
     SECTION("operational data")
     {
diff --git a/tests/example-schema.yang b/tests/example-schema.yang
index da45afd..372b83d 100644
--- a/tests/example-schema.yang
+++ b/tests/example-schema.yang
@@ -294,4 +294,13 @@
             type int32;
         }
     }
+
+    leaf flags {
+        type bits {
+            bit carry;
+            bit zero;
+            bit sign;
+            bit parity;
+        }
+    }
 }
diff --git a/tests/mock/sysrepo_subscription.cpp b/tests/mock/sysrepo_subscription.cpp
index c74d7af..ba276a7 100644
--- a/tests/mock/sysrepo_subscription.cpp
+++ b/tests/mock/sysrepo_subscription.cpp
@@ -6,6 +6,8 @@
  *
 */
 
+#include <experimental/iterator>
+#include <sstream>
 #include <sysrepo-cpp/Session.hpp>
 #include "sysrepo_subscription.hpp"
 
@@ -94,6 +96,14 @@
         v->set(xpath.c_str(), what.c_str());
     }
 
+    void operator()(const bits_& what)
+    {
+        std::stringstream ss;
+        std::copy(what.m_bits.begin(), what.m_bits.end(), std::experimental::make_ostream_joiner(ss, " "));
+        v->set(xpath.c_str(), ss.str().c_str());
+
+    }
+
     template <typename Type>
     void operator()(const Type what)
     {
diff --git a/tests/pretty_printers.hpp b/tests/pretty_printers.hpp
index 6864443..ab6b917 100644
--- a/tests/pretty_printers.hpp
+++ b/tests/pretty_printers.hpp
@@ -42,7 +42,7 @@
 {
     s << "DatastoreAccess::Tree {\n";
     for (const auto& [xpath, value] : tree) {
-        s << "    {" << xpath << ", " << leafDataToString(value) << "},\n";
+        s << "    {" << xpath << ", " << boost::core::demangle(value.type().name()) << "{" << leafDataToString(value) << "}},\n";
     }
     s << "}\n";
     return s;