Add support for binary values

Change-Id: I0ebc7d49590e3bffc449a781822e4d6bf423f331
diff --git a/src/ast_commands.cpp b/src/ast_commands.cpp
index fe2fa84..55b6ccd 100644
--- a/src/ast_commands.cpp
+++ b/src/ast_commands.cpp
@@ -14,6 +14,13 @@
 {
 }
 
+binary_::binary_() = default;
+
+binary_::binary_(const std::string& value)
+    : m_value(value)
+{
+}
+
 bool set_::operator==(const set_& b) const
 {
     return this->m_path == b.m_path && this->m_data == b.m_data;
@@ -29,6 +36,11 @@
     return this->m_path == b.m_path && this->m_options == b.m_options;
 }
 
+bool binary_::operator==(const binary_& b) const
+{
+    return this->m_value == b.m_value;
+}
+
 bool enum_::operator==(const enum_& b) const
 {
     return this->m_value == b.m_value;
diff --git a/src/ast_commands.hpp b/src/ast_commands.hpp
index acda9fa..634a190 100644
--- a/src/ast_commands.hpp
+++ b/src/ast_commands.hpp
@@ -193,8 +193,9 @@
 BOOST_FUSION_ADAPT_STRUCT(cd_, m_path)
 BOOST_FUSION_ADAPT_STRUCT(create_, m_path)
 BOOST_FUSION_ADAPT_STRUCT(delete_, m_path)
-BOOST_FUSION_ADAPT_STRUCT(enum_, m_value)
 BOOST_FUSION_ADAPT_STRUCT(set_, m_path, m_data)
+BOOST_FUSION_ADAPT_STRUCT(enum_, m_value)
+BOOST_FUSION_ADAPT_STRUCT(binary_, m_value)
 BOOST_FUSION_ADAPT_STRUCT(commit_)
 BOOST_FUSION_ADAPT_STRUCT(help_, m_cmd)
 BOOST_FUSION_ADAPT_STRUCT(discard_)
diff --git a/src/ast_handlers.hpp b/src/ast_handlers.hpp
index 96ccc53..a34f6c4 100644
--- a/src/ast_handlers.hpp
+++ b/src/ast_handlers.hpp
@@ -445,6 +445,15 @@
     }
 };
 
+struct leaf_data_binary_data_class;
+
+struct leaf_data_binary_class : leaf_data_base_class {
+    leaf_data_binary_class()
+        : leaf_data_base_class(yang::LeafDataTypes::Binary)
+    {
+    }
+};
+
 struct set_class {
     template <typename Iterator, typename Exception, typename Context>
     x3::error_handler_result on_error(Iterator&, Iterator const&, Exception const& x, Context const& context)
diff --git a/src/ast_values.hpp b/src/ast_values.hpp
index 369a73e..b9053c0 100644
--- a/src/ast_values.hpp
+++ b/src/ast_values.hpp
@@ -16,7 +16,15 @@
     std::string m_value;
 };
 
+struct binary_ {
+    binary_();
+    binary_(const std::string& value);
+    bool operator==(const binary_& b) const;
+    std::string m_value;
+};
+
 using leaf_data_ = boost::variant<enum_,
+                                  binary_,
                                   double,
                                   bool,
                                   int32_t,
diff --git a/src/grammars.hpp b/src/grammars.hpp
index b1c1b03..5878053 100644
--- a/src/grammars.hpp
+++ b/src/grammars.hpp
@@ -42,6 +42,8 @@
 x3::rule<leaf_data_int_class, int32_t> const leaf_data_int = "leaf_data_int";
 x3::rule<leaf_data_uint_class, uint32_t> const leaf_data_uint = "leaf_data_uint";
 x3::rule<leaf_data_string_class, std::string> const leaf_data_string = "leaf_data_string";
+x3::rule<leaf_data_binary_data_class, std::string> const leaf_data_binary_data = "leaf_data_binary_data";
+x3::rule<leaf_data_binary_class, binary_> const leaf_data_binary = "leaf_data_binary";
 
 x3::rule<discard_class, discard_> const discard = "discard";
 x3::rule<ls_class, ls_> const ls = "ls";
@@ -202,6 +204,13 @@
 auto const leaf_data_string_def =
     *char_;
 
+// This intermediate rule is neccessary for coercing to std::string.
+auto const leaf_data_binary_data_def =
+    +(x3::alnum | char_('+') | char_('/')) >> -char_('=') >> -char_('=');
+
+auto const leaf_data_binary_def =
+    leaf_data_binary_data;
+
 auto const leaf_data_def =
 x3::expect[
     leaf_data_enum |
@@ -209,6 +218,7 @@
     leaf_data_bool |
     leaf_data_int |
     leaf_data_uint |
+    leaf_data_binary |
     leaf_data_string];
 
 struct ls_options_table : x3::symbols<LsOption> {
@@ -298,6 +308,8 @@
 BOOST_SPIRIT_DEFINE(leaf_data_int)
 BOOST_SPIRIT_DEFINE(leaf_data_uint)
 BOOST_SPIRIT_DEFINE(leaf_data_string)
+BOOST_SPIRIT_DEFINE(leaf_data_binary_data)
+BOOST_SPIRIT_DEFINE(leaf_data_binary)
 BOOST_SPIRIT_DEFINE(initializePath)
 BOOST_SPIRIT_DEFINE(set)
 BOOST_SPIRIT_DEFINE(commit)
diff --git a/src/interpreter.cpp b/src/interpreter.cpp
index 5d22b6b..aff5bcc 100644
--- a/src/interpreter.cpp
+++ b/src/interpreter.cpp
@@ -15,6 +15,12 @@
     {
         return data.m_value;
     }
+
+    std::string operator()(const binary_& data) const
+    {
+        return data.m_value;
+    }
+
     template <typename T>
     std::string operator()(const T& data) const
     {
diff --git a/src/schema.hpp b/src/schema.hpp
index c55e659..aaeb846 100644
--- a/src/schema.hpp
+++ b/src/schema.hpp
@@ -28,6 +28,7 @@
     Int,
     Uint,
     Enum,
+    Binary,
 };
 
 struct container {
diff --git a/src/sysrepo_access.cpp b/src/sysrepo_access.cpp
index 5c80bbc..a6388db 100644
--- a/src/sysrepo_access.cpp
+++ b/src/sysrepo_access.cpp
@@ -43,6 +43,11 @@
         return std::make_shared<sysrepo::Val>(value.m_value.c_str(), SR_ENUM_T);
     }
 
+    sysrepo::S_Val operator()(const binary_& value) const
+    {
+        return std::make_shared<sysrepo::Val>(value.m_value.c_str(), SR_BINARY_T);
+    }
+
     sysrepo::S_Val operator()(const std::string& value) const
     {
         return std::make_shared<sysrepo::Val>(value.c_str());
diff --git a/src/yang_schema.cpp b/src/yang_schema.cpp
index 28ef7d6..f6e0eea 100644
--- a/src/yang_schema.cpp
+++ b/src/yang_schema.cpp
@@ -204,6 +204,8 @@
         return yang::LeafDataTypes::Uint;
     case LY_TYPE_ENUM:
         return yang::LeafDataTypes::Enum;
+    case LY_TYPE_BINARY:
+        return yang::LeafDataTypes::Binary;
     default:
         throw UnsupportedYangTypeException("the type of "s + fullNodeName(location, node) + " is not supported");
     }
diff --git a/tests/leaf_editing.cpp b/tests/leaf_editing.cpp
index 757a617..274d171 100644
--- a/tests/leaf_editing.cpp
+++ b/tests/leaf_editing.cpp
@@ -21,6 +21,7 @@
     schema->addLeaf("", "mod:leafBool", yang::LeafDataTypes::Bool);
     schema->addLeaf("", "mod:leafInt", yang::LeafDataTypes::Int);
     schema->addLeaf("", "mod:leafUint", yang::LeafDataTypes::Uint);
+    schema->addLeaf("", "mod:leafBinary", yang::LeafDataTypes::Binary);
     schema->addLeafEnum("", "mod:leafEnum", {"lol", "data", "coze"});
     schema->addLeaf("mod:contA", "mod:leafInCont", yang::LeafDataTypes::String);
     schema->addList("", "mod:list", {"number"});
@@ -33,7 +34,7 @@
     {
         set_ expected;
 
-        SECTION("set leafString some_data")
+        SECTION("set mod:leafString some_data")
         {
             input = "set mod:leafString some_data";
             expected.m_path.m_nodes.push_back(dataNode_{module_{"mod"}, leaf_("leafString")});
@@ -48,7 +49,7 @@
             expected.m_data = std::string("more_data");
         }
 
-        SECTION("set list[number=1]/leafInList another_data")
+        SECTION("set mod:list[number=1]/leafInList another_data")
         {
             input = "set mod:list[number=1]/leafInList another_data";
             auto keys = std::map<std::string, std::string>{
@@ -94,6 +95,28 @@
                 expected.m_path.m_nodes.push_back(dataNode_{module_{"mod"}, leaf_("leafBool")});
                 expected.m_data = true;
             }
+
+            SECTION("binary")
+            {
+                SECTION("zero ending '='")
+                {
+                    input = "set mod:leafBinary This/IsABase64EncodedSomething++/342431++";
+                    expected.m_data = binary_{"This/IsABase64EncodedSomething++/342431++"};
+                }
+
+                SECTION("one ending '='")
+                {
+                    input = "set mod:leafBinary This/IsABase64EncodedSomething++/342431++=";
+                    expected.m_data = binary_{"This/IsABase64EncodedSomething++/342431++="};
+                }
+
+                SECTION("two ending '='")
+                {
+                    input = "set mod:leafBinary This/IsABase64EncodedSomething++/342431++==";
+                    expected.m_data = binary_{"This/IsABase64EncodedSomething++/342431++=="};
+                }
+                expected.m_path.m_nodes.push_back(dataNode_{module_{"mod"}, leaf_("leafBinary")});
+            }
         }
 
         command_ command = parser.parseCommand(input, errorStream);
@@ -152,6 +175,14 @@
             }
         }
 
+        SECTION("wrong base64 strings")
+        {
+            SECTION("invalid character")
+                input = "set leafBinary dbahj-";
+            SECTION("equal sign in the middle")
+                input = "set leafBinary db=ahj";
+        }
+
         REQUIRE_THROWS_AS(parser.parseCommand(input, errorStream), InvalidCommandException&);
     }
 }