Add a function for staticSuggestions

This new function wraps the boilerplate that's needed for making static
suggestion list. This could also be used for the dump_args parser (to
get rid of more boilerplate), but it's a bigger change, so I'll leave
that for another patch.

Also, the parser really NEEDS its attribute to be the unused_type,
otherwise weird stuff happens, ghosts appear and it also messes with the
attributes of other parsers. I don't know why, but oh well.

This thing has to copy because a std::initializer_list cannot be
properly captured by value in the inner lambda. Such a code builds, but
it won't do the right thing.

Change-Id: Ida0bcc86124b88d686ac86cf4743d220db86f59b
Co-authored-by: Jan Kundrát <jan.kundrat@cesnet.cz>
diff --git a/src/grammars.hpp b/src/grammars.hpp
index a6df5c9..71af580 100644
--- a/src/grammars.hpp
+++ b/src/grammars.hpp
@@ -15,6 +15,25 @@
 #include "leaf_data.hpp"
 #include "path_parser.hpp"
 
+/**
+ * Returns a parser that generates suggestions based on a Completion set.
+ * Usage:
+ * const auto suggestSomeStuff = staticSuggestions({"some", "stuff", "yay"});
+ *
+ * You can use this as a standard parser in a Spirit grammar.
+ */
+auto staticSuggestions(const std::initializer_list<std::string>& strings)
+{
+    std::set<Completion> completions;
+    std::transform(begin(strings), end(strings), std::inserter(completions, completions.end()),
+            [](const auto s) { return Completion{s, " "}; });
+    return as<x3::unused_type>[x3::eps[([completions](auto& ctx) {
+        auto& parserContext = x3::get<parser_context_tag>(ctx);
+        parserContext.m_suggestions = completions;
+        parserContext.m_completionIterator = _where(ctx).begin();
+    })]];
+}
+
 #if BOOST_VERSION <= 107700
 namespace boost::spirit::x3::traits
 {
@@ -148,11 +167,7 @@
 const auto copy_source = x3::rule<class source, Datastore>{"source datastore"} = datastore;
 const 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();
-})];
+const auto datastoreSuggestions = staticSuggestions({"running", "startup"});
 
 struct copy_args : x3::parser<copy_args> {
     using attribute_type = copy_;
@@ -317,11 +332,7 @@
 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();
-})];
+const auto dsTargetSuggestions = staticSuggestions({"running", "startup", "operational"});
 
 struct ds_target_table : x3::symbols<DatastoreTarget> {
     ds_target_table()
@@ -334,7 +345,7 @@
 } const ds_target_table;
 
 auto const switch_rule_def =
-    switch_::name > space_separator > as<x3::unused_type>[dsTargetSuggestions] > ds_target_table;
+    switch_::name > space_separator > dsTargetSuggestions > ds_target_table;
 
 auto const cancel_def =
     cancel_::name >> x3::attr(cancel_{});