Add `switch` command

Change-Id: I5db53925f1710e29b49398621e6b634889f1df15
diff --git a/src/ast_commands.cpp b/src/ast_commands.cpp
index fa2b56c..74aefd5 100644
--- a/src/ast_commands.cpp
+++ b/src/ast_commands.cpp
@@ -56,3 +56,8 @@
 {
     return this->m_path == other.m_path;
 }
+
+bool switch_::operator==(const switch_& other) const
+{
+    return this->m_target == other.m_target;
+}
diff --git a/src/ast_commands.hpp b/src/ast_commands.hpp
index 9ec2740..d0a3f3e 100644
--- a/src/ast_commands.hpp
+++ b/src/ast_commands.hpp
@@ -11,6 +11,7 @@
 #include <boost/spirit/home/x3/support/ast/position_tagged.hpp>
 #include "ast_path.hpp"
 #include "ast_values.hpp"
+#include "datastore_access.hpp"
 #include "yang_operations.hpp"
 
 namespace x3 = boost::spirit::x3;
@@ -287,8 +288,32 @@
     bool operator==(const cancel_& other) const;
 };
 
+struct switch_ : x3::position_tagged {
+    static constexpr auto name = "switch";
+    static constexpr auto shortHelp = "switch - Switch datastore target.";
+    static constexpr auto longHelp = R"(
+    switch <target>
+
+    This command switches the datastore target. Available targets are:
+
+    operational:
+        - reads from operational, writes to running
+    startup:
+        - reads from startup, writes to startup
+    running:
+        - reads from running, writes to running
+
+
+    Usage:
+        /> switch running
+        /> switch startup
+        /> switch operational)";
+    bool operator==(const switch_& other) const;
+    DatastoreTarget m_target;
+};
+
 struct help_;
-using CommandTypes = boost::mpl::vector<cancel_, cd_, commit_, copy_, create_, delete_, describe_, discard_, dump_, exec_, get_, help_, ls_, move_, prepare_, set_>;
+using CommandTypes = boost::mpl::vector<cancel_, cd_, commit_, copy_, create_, delete_, describe_, discard_, dump_, exec_, get_, help_, ls_, move_, prepare_, set_, switch_>;
 struct help_ : x3::position_tagged {
     static constexpr auto name = "help";
     static constexpr auto shortHelp = "help - Print help for commands.";
@@ -339,3 +364,4 @@
 BOOST_FUSION_ADAPT_STRUCT(dump_, m_format)
 BOOST_FUSION_ADAPT_STRUCT(prepare_, m_path)
 BOOST_FUSION_ADAPT_STRUCT(exec_, m_path)
+BOOST_FUSION_ADAPT_STRUCT(switch_, m_target)
diff --git a/src/ast_handlers.hpp b/src/ast_handlers.hpp
index 686af85..288801d 100644
--- a/src/ast_handlers.hpp
+++ b/src/ast_handlers.hpp
@@ -270,6 +270,8 @@
 
 struct exec_class;
 
+struct switch_class;
+
 struct cancel_class;
 
 struct command_class {
diff --git a/src/grammars.hpp b/src/grammars.hpp
index e86748c..efc9a54 100644
--- a/src/grammars.hpp
+++ b/src/grammars.hpp
@@ -31,6 +31,7 @@
 x3::rule<dump_class, dump_> const dump = "dump";
 x3::rule<prepare_class, prepare_> const prepare = "prepare";
 x3::rule<exec_class, exec_> const exec = "exec";
+x3::rule<switch_class, exec_> const switch_rule = "switch";
 x3::rule<cancel_class, cancel_> const cancel = "cancel";
 x3::rule<command_class, command_> const command = "command";
 
@@ -139,8 +140,8 @@
 auto const describe_def =
     describe_::name >> space_separator > anyPath;
 
-struct mode_table : x3::symbols<MoveMode> {
-    mode_table()
+struct move_mode_table : x3::symbols<MoveMode> {
+    move_mode_table()
     {
         add
             ("after", MoveMode::After)
@@ -148,7 +149,7 @@
             ("begin", MoveMode::Begin)
             ("end", MoveMode::End);
     }
-} const mode_table;
+} const move_mode_table;
 
 struct move_absolute_table : x3::symbols<yang::move::Absolute> {
     move_absolute_table()
@@ -264,6 +265,25 @@
 auto const exec_def =
     exec_::name > -(space_separator > -as<dataPath_>[RpcActionPath<AllowInput::No>{}]);
 
+const auto dsTargetSuggestions = x3::eps[([](auto& ctx) {
+    auto& parserContext = x3::get<parser_context_tag>(ctx);
+    parserContext.m_suggestions = {Completion{"running", " "}, Completion{"startup", " "}, Completion{"operational", " "}};
+    parserContext.m_completionIterator = _where(ctx).begin();
+})];
+
+struct ds_target_table : x3::symbols<DatastoreTarget> {
+    ds_target_table()
+    {
+        add
+            ("operational", DatastoreTarget::Operational)
+            ("startup", DatastoreTarget::Startup)
+            ("running", DatastoreTarget::Running);
+    }
+} const ds_target_table;
+
+auto const switch_rule_def =
+    switch_::name > space_separator > dsTargetSuggestions > ds_target_table;
+
 auto const cancel_def =
     cancel_::name >> x3::attr(cancel_{});
 
@@ -274,7 +294,7 @@
     x3::eps;
 
 auto const command_def =
-    createCommandSuggestions >> x3::expect[cd | copy | create | delete_rule | set | commit | get | ls | discard | describe | help | move | dump | prepare | exec | cancel];
+    createCommandSuggestions >> x3::expect[cd | copy | create | delete_rule | set | commit | get | ls | discard | describe | help | move | dump | prepare | exec | cancel | switch_rule];
 
 #if __clang__
 #pragma GCC diagnostic pop
@@ -295,6 +315,7 @@
 BOOST_SPIRIT_DEFINE(dump)
 BOOST_SPIRIT_DEFINE(prepare)
 BOOST_SPIRIT_DEFINE(exec)
+BOOST_SPIRIT_DEFINE(switch_rule)
 BOOST_SPIRIT_DEFINE(cancel)
 BOOST_SPIRIT_DEFINE(command)
 BOOST_SPIRIT_DEFINE(createCommandSuggestions)
diff --git a/src/interpreter.cpp b/src/interpreter.cpp
index 3f8edc7..43e2269 100644
--- a/src/interpreter.cpp
+++ b/src/interpreter.cpp
@@ -247,6 +247,11 @@
     m_datastore.cancel();
 }
 
+void Interpreter::operator()(const switch_& switch_cmd) const
+{
+    m_datastore.setTarget(switch_cmd.m_target);
+}
+
 struct commandLongHelpVisitor : boost::static_visitor<const char*> {
     template <typename T>
     auto constexpr operator()(boost::type<T>) const
diff --git a/src/interpreter.hpp b/src/interpreter.hpp
index c59e3d2..36c9388 100644
--- a/src/interpreter.hpp
+++ b/src/interpreter.hpp
@@ -32,6 +32,7 @@
     void operator()(const prepare_& prepare) const;
     void operator()(const exec_& exec) const;
     void operator()(const cancel_& cancel) const;
+    void operator()(const switch_& switch_cmd) const;
 
 private:
     [[nodiscard]] std::string buildTypeInfo(const std::string& path) const;
diff --git a/src/proxy_datastore.cpp b/src/proxy_datastore.cpp
index 8f86831..3bd6719 100644
--- a/src/proxy_datastore.cpp
+++ b/src/proxy_datastore.cpp
@@ -107,3 +107,8 @@
         return m_inputDatastore;
     }
 }
+
+void ProxyDatastore::setTarget(const DatastoreTarget target)
+{
+    m_datastore->setTarget(target);
+}
diff --git a/src/proxy_datastore.hpp b/src/proxy_datastore.hpp
index f084a2d..21fbfc5 100644
--- a/src/proxy_datastore.hpp
+++ b/src/proxy_datastore.hpp
@@ -28,6 +28,7 @@
     void discardChanges();
     void copyConfig(const Datastore source, const Datastore destination);
     [[nodiscard]] std::string dump(const DataFormat format) const;
+    void setTarget(const DatastoreTarget target);
 
     void initiate(const std::string& path);
     [[nodiscard]] DatastoreAccess::Tree execute();
diff --git a/tests/command_completion.cpp b/tests/command_completion.cpp
index c92bad0..8e8570f 100644
--- a/tests/command_completion.cpp
+++ b/tests/command_completion.cpp
@@ -21,7 +21,7 @@
     int expectedContextLength;
     SECTION("no prefix")
     {
-        expectedCompletions = {"cd", "copy", "create", "delete", "set", "commit", "get", "ls", "discard", "help", "describe", "move", "dump", "prepare", "exec", "cancel"};
+        expectedCompletions = {"cd", "copy", "create", "delete", "set", "commit", "get", "ls", "discard", "help", "describe", "move", "dump", "prepare", "exec", "cancel", "switch"};
         expectedContextLength = 0;
         SECTION("no space")
         {
@@ -75,6 +75,13 @@
         expectedContextLength = 0;
     }
 
+    SECTION("switch datastore targets")
+    {
+        input = "switch ";
+        expectedCompletions = {"running", "startup", "operational"};
+        expectedContextLength = 0;
+    }
+
     SECTION("dump")
     {
         input = "dump ";