Add quit command

Change-Id: Ia0a01a558ec92bb0d8f22fe2fbddf74b8d78242b
diff --git a/src/ast_commands.hpp b/src/ast_commands.hpp
index 0a474f0..ccbe266 100644
--- a/src/ast_commands.hpp
+++ b/src/ast_commands.hpp
@@ -315,8 +315,21 @@
     DatastoreTarget m_target;
 };
 
+struct quit_ : x3::position_tagged {
+    static constexpr auto name = "quit";
+    static constexpr auto shortHelp = "quit - Quit the console.";
+    static constexpr auto longHelp = R"(
+    quit
+
+    Quit the console. Accepts no arguments.
+
+    Usage:
+        /> quit)";
+    bool operator==(const quit_& b) const;
+};
+
 struct help_;
-using CommandTypes = boost::mpl::vector<cancel_, cd_, commit_, copy_, create_, delete_, describe_, discard_, dump_, exec_, get_, help_, ls_, move_, prepare_, set_, switch_>;
+using CommandTypes = boost::mpl::vector<cancel_, cd_, commit_, copy_, create_, delete_, describe_, discard_, dump_, exec_, get_, help_, ls_, move_, prepare_, quit_, set_, switch_>;
 struct help_ : x3::position_tagged {
     static constexpr auto name = "help";
     static constexpr auto shortHelp = "help - Print help for commands.";
@@ -368,3 +381,4 @@
 BOOST_FUSION_ADAPT_STRUCT(prepare_, m_path)
 BOOST_FUSION_ADAPT_STRUCT(exec_, m_path)
 BOOST_FUSION_ADAPT_STRUCT(switch_, m_target)
+BOOST_FUSION_ADAPT_STRUCT(quit_)
diff --git a/src/ast_handlers.hpp b/src/ast_handlers.hpp
index 352824e..5cbb993 100644
--- a/src/ast_handlers.hpp
+++ b/src/ast_handlers.hpp
@@ -311,6 +311,8 @@
 
 struct cancel_class;
 
+struct quit_class;
+
 struct command_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/cli.cpp b/src/cli.cpp
index 3ed40e8..9827fa3 100644
--- a/src/cli.cpp
+++ b/src/cli.cpp
@@ -293,6 +293,9 @@
         try {
             command_ cmd = parser.parseCommand(line, std::cout);
             boost::apply_visitor(Interpreter(parser, proxyDatastore), cmd);
+            if (cmd.type() == typeid(quit_)) {
+                break;
+            }
         } catch (InvalidCommandException& ex) {
             std::cerr << ex.what() << std::endl;
         } catch (DatastoreException& ex) {
diff --git a/src/grammars.hpp b/src/grammars.hpp
index 4e3b97c..b8d9456 100644
--- a/src/grammars.hpp
+++ b/src/grammars.hpp
@@ -84,6 +84,7 @@
 x3::rule<exec_class, exec_> const exec = "exec";
 x3::rule<switch_class, switch_> const switch_rule = "switch";
 x3::rule<cancel_class, cancel_> const cancel = "cancel";
+x3::rule<quit_class, quit_> const quit = "quit";
 x3::rule<command_class, command_> const command = "command";
 
 x3::rule<createCommandSuggestions_class, x3::unused_type> const createCommandSuggestions = "createCommandSuggestions";
@@ -354,6 +355,9 @@
 auto const dump_def =
     dump_::name > space_separator >> dump_args;
 
+auto const quit_def =
+    quit_::name >> x3::attr(quit_{});
+
 auto const createCommandSuggestions_def =
     x3::eps;
 
@@ -361,7 +365,7 @@
 #if BOOST_VERSION <= 107800
     x3::eps >>
 #endif
-    createCommandSuggestions >> x3::expect[cd | copy | create | delete_rule | set | commit | get | ls | discard | describe | help | move | dump | prepare | exec | cancel | switch_rule];
+    createCommandSuggestions >> x3::expect[cd | copy | create | delete_rule | set | commit | get | ls | discard | describe | help | move | dump | prepare | exec | cancel | switch_rule | quit];
 
 #if __clang__
 #pragma GCC diagnostic pop
@@ -384,5 +388,6 @@
 BOOST_SPIRIT_DEFINE(exec)
 BOOST_SPIRIT_DEFINE(switch_rule)
 BOOST_SPIRIT_DEFINE(cancel)
+BOOST_SPIRIT_DEFINE(quit)
 BOOST_SPIRIT_DEFINE(command)
 BOOST_SPIRIT_DEFINE(createCommandSuggestions)
diff --git a/src/interpreter.cpp b/src/interpreter.cpp
index 0fe96da..d139e8b 100644
--- a/src/interpreter.cpp
+++ b/src/interpreter.cpp
@@ -291,6 +291,11 @@
     }
 }
 
+void Interpreter::operator()(const quit_&) const
+{
+    // no operation
+}
+
 template <typename PathType>
 boost::variant<dataPath_, schemaPath_, module_> Interpreter::toCanonicalPath(const boost::optional<PathType>& optPath) const
 {
diff --git a/src/interpreter.hpp b/src/interpreter.hpp
index 36c9388..f2cfd11 100644
--- a/src/interpreter.hpp
+++ b/src/interpreter.hpp
@@ -33,6 +33,7 @@
     void operator()(const exec_& exec) const;
     void operator()(const cancel_& cancel) const;
     void operator()(const switch_& switch_cmd) const;
+    void operator()(const quit_&) const;
 
 private:
     [[nodiscard]] std::string buildTypeInfo(const std::string& path) const;