Add help command

Change-Id: Ifbda5a7aafefe396854c00add315fb7a67d4fb26
diff --git a/src/ast_commands.hpp b/src/ast_commands.hpp
index 3a59e26..acda9fa 100644
--- a/src/ast_commands.hpp
+++ b/src/ast_commands.hpp
@@ -36,11 +36,32 @@
 
 struct discard_ : x3::position_tagged {
     static constexpr auto name = "discard";
+    static constexpr auto shortHelp = "discard - Discard current changes.";
+    static constexpr auto longHelp = R"(
+    discard
+
+    Discards current changes. Accepts no arguments.
+
+    Usage:
+        /> discard)";
     bool operator==(const discard_& b) const;
 };
 
 struct ls_ : x3::position_tagged {
     static constexpr auto name = "ls";
+    static constexpr auto shortHelp = "ls - List available nodes.";
+    static constexpr auto longHelp = R"(
+    ls [--recursive] [path]
+
+    Lists available nodes in the current directory. Optionally
+    accepts a path argument. Accepts both schema paths and data
+    paths. Path starting with a forward slash means an absolute
+    path.
+
+    Usage:
+        /> ls
+        /> ls --recursive module:node
+        /> ls /module:node)";
     bool operator==(const ls_& b) const;
     std::vector<LsOption> m_options;
     boost::optional<boost::variant<dataPath_, schemaPath_>> m_path;
@@ -48,24 +69,58 @@
 
 struct cd_ : x3::position_tagged {
     static constexpr auto name = "cd";
+    static constexpr auto shortHelp = "cd - Enter a different node.";
+    static constexpr auto longHelp = R"(
+    cd path
+
+    Enters a node specified by path. Only accepts data paths.
+
+    Usage:
+        /> cd /module:node/node2
+        /> cd ..)";
     bool operator==(const cd_& b) const;
     dataPath_ m_path;
 };
 
 struct create_ : x3::position_tagged {
     static constexpr auto name = "create";
+    static constexpr auto shortHelp = "create - Create a presence container.";
+    static constexpr auto longHelp = R"(
+    create path_to_presence_container
+
+    Creates a presence container specified by a path.
+
+    Usage:
+        /> create /module:pContainer)";
     bool operator==(const create_& b) const;
     dataPath_ m_path;
 };
 
 struct delete_ : x3::position_tagged {
     static constexpr auto name = "delete";
+    static constexpr auto shortHelp = "delete - Delete a presence container.";
+    static constexpr auto longHelp = R"(
+    delete path_to_presence_container
+
+    Delete a presence container specified by a path.
+
+    Usage:
+        /> delete /module:pContainer)";
     bool operator==(const delete_& b) const;
     dataPath_ m_path;
 };
 
 struct set_ : x3::position_tagged {
     static constexpr auto name = "set";
+    static constexpr auto shortHelp = "set - Change value of a leaf.";
+    static constexpr auto longHelp = R"(
+    set path_to_leaf value
+
+    Changes the leaf specified by path to value.
+
+    Usage:
+        /> set /module:leaf 123
+        /> set /module:leaf abc)";
     bool operator==(const set_& b) const;
     dataPath_ m_path;
     leaf_data_ m_data;
@@ -73,18 +128,65 @@
 
 struct commit_ : x3::position_tagged {
     static constexpr auto name = "commit";
+    static constexpr auto shortHelp = "commit - Commit current changes.";
+    static constexpr auto longHelp = R"(
+    commit
+
+    Commits the current changes. Accepts no arguments.
+
+    Usage:
+        /> commit)";
     bool operator==(const set_& b) const;
 };
 
 struct get_ : x3::position_tagged {
     static constexpr auto name = "get";
+    static constexpr auto shortHelp = "get - Retrieve configuration from the server.";
+    static constexpr auto longHelp = R"(
+    get [path]
+
+    Retrieves configuration of the current node. Works recursively.
+    Optionally takes an argument specifying a path, the output will,
+    as if the user was in that node.
+
+    Usage:
+        /> get
+        /> get /module:path)";
     bool operator==(const get_& b) const;
     boost::optional<boost::variant<dataPath_, schemaPath_>> m_path;
 };
 
+struct help_;
+using CommandTypes = boost::mpl::vector<discard_, ls_, cd_, create_, delete_, set_, commit_, get_, help_>;
+struct help_ : x3::position_tagged {
+    static constexpr auto name = "help";
+    static constexpr auto shortHelp = "help - Print help for commands.";
+    static constexpr auto longHelp = R"(
+    help [command_name]
+
+    Print help for command_name. If used without an argument,
+    print short help for all commands.
+
+    Usage:
+        /> help
+        /> help cd
+        /> help help)";
+    bool operator==(const help_& b) const;
+
+    // The help command has got one optional argument – a command name (type).
+    // All commands are saved in CommandTypes, so we could just use that, but
+    // that way, Spirit would be default constructing the command structs,
+    // which is undesirable, so firstly we use mpl::transform to wrap
+    // CommandTypes with boost::type:
+    using WrappedCommandTypes = boost::mpl::transform<CommandTypes, boost::type<boost::mpl::_>>::type;
+    // Next, we create a variant over the wrapped types:
+    using CommandTypesVariant = boost::make_variant_over<WrappedCommandTypes>::type;
+    // Finally, we wrap the variant with boost::optional:
+    boost::optional<CommandTypesVariant> m_cmd;
+};
+
 // TODO: The usage of MPL won't be necessary after std::variant support is added to Spirit
 // https://github.com/boostorg/spirit/issues/270
-using CommandTypes = boost::mpl::vector<discard_, ls_, cd_, create_, delete_, set_, commit_, get_>;
 using command_ = boost::make_variant_over<CommandTypes>::type;
 
 BOOST_FUSION_ADAPT_STRUCT(ls_, m_options, m_path)
@@ -94,5 +196,6 @@
 BOOST_FUSION_ADAPT_STRUCT(enum_, m_value)
 BOOST_FUSION_ADAPT_STRUCT(set_, m_path, m_data)
 BOOST_FUSION_ADAPT_STRUCT(commit_)
+BOOST_FUSION_ADAPT_STRUCT(help_, m_cmd)
 BOOST_FUSION_ADAPT_STRUCT(discard_)
 BOOST_FUSION_ADAPT_STRUCT(get_, m_path)
diff --git a/src/ast_handlers.hpp b/src/ast_handlers.hpp
index 7eed8be..96ccc53 100644
--- a/src/ast_handlers.hpp
+++ b/src/ast_handlers.hpp
@@ -458,6 +458,8 @@
 
 struct commit_class;
 
+struct help_class;
+
 struct get_class;
 
 struct command_class {
diff --git a/src/grammars.hpp b/src/grammars.hpp
index c12f922..b1c1b03 100644
--- a/src/grammars.hpp
+++ b/src/grammars.hpp
@@ -51,6 +51,7 @@
 x3::rule<create_class, create_> const create = "create";
 x3::rule<delete_class, delete_> const delete_rule = "delete_rule";
 x3::rule<commit_class, commit_> const commit = "commit";
+x3::rule<help_class, help_> const help = "help";
 x3::rule<command_class, command_> const command = "command";
 
 x3::rule<initializePath_class, x3::unused_type> const initializePath = "initializePath";
@@ -246,11 +247,23 @@
 auto const discard_def =
     discard_::name >> x3::attr(discard_());
 
+struct command_names_table : x3::symbols<decltype(help_::m_cmd)> {
+    command_names_table()
+    {
+        boost::mpl::for_each<CommandTypes, boost::type<boost::mpl::_>>([this](auto cmd) {
+            add(commandNamesVisitor()(cmd), decltype(help_::m_cmd)(cmd));
+        });
+    }
+} const command_names;
+
+auto const help_def =
+    help_::name > createCommandSuggestions >> -command_names;
+
 auto const createCommandSuggestions_def =
     x3::eps;
 
 auto const command_def =
-    createCommandSuggestions >> x3::expect[cd | create | delete_rule | set | commit | get | ls | discard];
+    createCommandSuggestions >> x3::expect[cd | create | delete_rule | set | commit | get | ls | discard | help];
 
 #if __clang__
 #pragma GCC diagnostic pop
@@ -294,6 +307,7 @@
 BOOST_SPIRIT_DEFINE(cd)
 BOOST_SPIRIT_DEFINE(create)
 BOOST_SPIRIT_DEFINE(delete_rule)
+BOOST_SPIRIT_DEFINE(help)
 BOOST_SPIRIT_DEFINE(command)
 BOOST_SPIRIT_DEFINE(createPathSuggestions)
 BOOST_SPIRIT_DEFINE(createKeySuggestions)
diff --git a/src/interpreter.cpp b/src/interpreter.cpp
index 80e6195..5d22b6b 100644
--- a/src/interpreter.cpp
+++ b/src/interpreter.cpp
@@ -75,6 +75,32 @@
         std::cout << it << std::endl;
 }
 
+struct commandLongHelpVisitor : boost::static_visitor<const char*> {
+    template <typename T>
+    auto constexpr operator()(boost::type<T>) const
+    {
+        return T::longHelp;
+    }
+};
+
+struct commandShortHelpVisitor : boost::static_visitor<const char*> {
+    template <typename T>
+    auto constexpr operator()(boost::type<T>) const
+    {
+        return T::shortHelp;
+    }
+};
+
+void Interpreter::operator()(const help_& help) const
+{
+    if (help.m_cmd)
+        std::cout << boost::apply_visitor(commandLongHelpVisitor(), help.m_cmd.get()) << std::endl;
+    else
+        boost::mpl::for_each<CommandTypes, boost::type<boost::mpl::_>>([](auto cmd) {
+            std::cout << commandShortHelpVisitor()(cmd) << std::endl;
+        });
+}
+
 template <typename T>
 std::string Interpreter::absolutePathFromCommand(const T& command) const
 {
diff --git a/src/interpreter.hpp b/src/interpreter.hpp
index 0940ad5..7af5983 100644
--- a/src/interpreter.hpp
+++ b/src/interpreter.hpp
@@ -23,6 +23,7 @@
     void operator()(const delete_&) const;
     void operator()(const ls_&) const;
     void operator()(const discard_&) const;
+    void operator()(const help_&) const;
 
 private:
     template <typename T>
diff --git a/tests/command_completion.cpp b/tests/command_completion.cpp
index 21410c8..83ade0a 100644
--- a/tests/command_completion.cpp
+++ b/tests/command_completion.cpp
@@ -20,13 +20,13 @@
     SECTION("")
     {
         input = "";
-        expected = {"cd", "create", "delete", "set", "commit", "get", "ls", "discard"};
+        expected = {"cd", "create", "delete", "set", "commit", "get", "ls", "discard", "help"};
     }
 
     SECTION(" ")
     {
         input = " ";
-        expected = {"cd", "create", "delete", "set", "commit", "get", "ls", "discard"};
+        expected = {"cd", "create", "delete", "set", "commit", "get", "ls", "discard", "help"};
     }
 
     SECTION("c")