Add copy command

Change-Id: I0a88f7fa9a096022dd95e8af8854f980ca34f043
diff --git a/src/grammars.hpp b/src/grammars.hpp
index 9e4e35f..66fcfb3 100644
--- a/src/grammars.hpp
+++ b/src/grammars.hpp
@@ -36,7 +36,7 @@
 x3::rule<leaf_path_class, dataPath_> const leafPath = "leafPath";
 x3::rule<presenceContainerPath_class, dataPath_> const presenceContainerPath = "presenceContainerPath";
 x3::rule<listInstancePath_class, dataPath_> const listInstancePath = "listInstancePath";
-x3::rule<space_separator_class, x3::unused_type> const space_separator = "space_separator";
+x3::rule<space_separator_class, x3::unused_type> const space_separator = "a space";
 
 x3::rule<discard_class, discard_> const discard = "discard";
 x3::rule<ls_class, ls_> const ls = "ls";
@@ -48,6 +48,7 @@
 x3::rule<commit_class, commit_> const commit = "commit";
 x3::rule<describe_class, describe_> const describe = "describe";
 x3::rule<help_class, help_> const help = "help";
+x3::rule<copy_class, copy_> const copy = "copy";
 x3::rule<command_class, command_> const command = "command";
 
 x3::rule<initializePath_class, x3::unused_type> const initializePath = "initializePath";
@@ -217,6 +218,55 @@
 auto const help_def =
     help_::name > createCommandSuggestions >> -command_names;
 
+struct datastore_symbol_table : x3::symbols<Datastore> {
+    datastore_symbol_table()
+    {
+        add
+            ("running", Datastore::Running)
+            ("startup", Datastore::Startup);
+    }
+} const datastore;
+
+auto copy_source = x3::rule<class source, Datastore>{"source datastore"} = datastore;
+auto copy_destination = x3::rule<class source, Datastore>{"destination datastore"} = datastore;
+
+const auto datastoreSuggestions = x3::eps[([](auto& ctx) {
+    auto& parserContext = x3::get<parser_context_tag>(ctx);
+    parserContext.m_suggestions = {Completion{"running", " "}, Completion{"startup", " "}};
+    parserContext.m_completionIterator = _where(ctx).begin();
+})];
+
+struct copy_args : x3::parser<copy_args> {
+    using attribute_type = copy_;
+    template <typename It, typename Ctx, typename RCtx>
+    bool parse(It& begin, It end, Ctx const& ctx, RCtx& rctx, copy_& attr) const
+    {
+        auto& parserContext = x3::get<parser_context_tag>(ctx);
+        auto iterBeforeDestination = begin;
+        auto save_iter = x3::no_skip[x3::eps[([&iterBeforeDestination](auto& ctx) {iterBeforeDestination = _where(ctx).begin();})]];
+        auto grammar = datastoreSuggestions > copy_source > space_separator > datastoreSuggestions > save_iter > copy_destination;
+
+        try {
+            grammar.parse(begin, end, ctx, rctx, attr);
+        } catch (x3::expectation_failure<It>& ex) {
+            using namespace std::string_literals;
+            parserContext.m_errorMsg = "Expected "s + ex.which() + " here:";
+            throw;
+        }
+
+        if (attr.m_source == attr.m_destination) {
+            begin = iterBeforeDestination; // Restoring the iterator here makes the error caret point to the second datastore
+            parserContext.m_errorMsg = "Source datastore and destination datastore can't be the same.";
+            return false;
+        }
+
+        return true;
+    }
+} copy_args;
+
+auto const copy_def =
+    copy_::name > space_separator > copy_args;
+
 auto const describe_def =
     describe_::name >> space_separator > (dataPathListEnd | dataPath | schemaPath);
 
@@ -224,7 +274,7 @@
     x3::eps;
 
 auto const command_def =
-    createCommandSuggestions >> x3::expect[cd | create | delete_rule | set | commit | get | ls | discard | describe | help];
+    createCommandSuggestions >> x3::expect[cd | copy | create | delete_rule | set | commit | get | ls | discard | describe | help];
 
 #if __clang__
 #pragma GCC diagnostic pop
@@ -263,6 +313,7 @@
 BOOST_SPIRIT_DEFINE(delete_rule)
 BOOST_SPIRIT_DEFINE(describe)
 BOOST_SPIRIT_DEFINE(help)
+BOOST_SPIRIT_DEFINE(copy)
 BOOST_SPIRIT_DEFINE(command)
 BOOST_SPIRIT_DEFINE(createPathSuggestions)
 BOOST_SPIRIT_DEFINE(createKeySuggestions)