Integrate DataQuery

Change-Id: I439374afe485baf08a4d5a1a02fd70d908bec9e1
diff --git a/src/ast_handlers.cpp b/src/ast_handlers.cpp
index ca5fe25..8aa6470 100644
--- a/src/ast_handlers.cpp
+++ b/src/ast_handlers.cpp
@@ -1,10 +1,13 @@
 #include "ast_handlers.hpp"
-std::set<std::string> generateMissingKeyCompletionSet(std::set<std::string> keysNeeded, std::set<std::string> currentSet)
+std::set<std::string> generateMissingKeyCompletionSet(std::set<std::string> keysNeeded, std::map<std::string, leaf_data_> currentKeys)
 {
     std::set<std::string> missingKeys;
-    std::set_difference(keysNeeded.begin(), keysNeeded.end(),
-            currentSet.begin(), currentSet.end(),
-            std::inserter(missingKeys, missingKeys.end()));
+
+    for (const auto& key : keysNeeded) {
+        if (currentKeys.find(key) == currentKeys.end()) {
+            missingKeys.insert(key);
+        }
+    }
 
     std::set<std::string> res;
 
@@ -13,3 +16,12 @@
                    [] (auto it) { return it + "="; });
     return res;
 }
+
+std::string leafDataToCompletion(const leaf_data_& value)
+{
+    // Only string-like values need to be quoted
+    if (value.type() == typeid(std::string)) {
+        return escapeListKeyString(leafDataToString(value));
+    }
+    return leafDataToString(value);
+}
diff --git a/src/ast_handlers.hpp b/src/ast_handlers.hpp
index e59fc36..6ecfbc5 100644
--- a/src/ast_handlers.hpp
+++ b/src/ast_handlers.hpp
@@ -30,7 +30,7 @@
             _pass(context) = false;
             parserContext.m_errorMsg = "Key \"" + ast.first + "\" was entered more than once.";
         } else {
-            parserContext.m_tmpListKeys.insert(ast.first);
+            parserContext.m_tmpListKeys.insert({ast.first, ast.second});
         }
     }
 
@@ -68,11 +68,11 @@
 
         if (schema.listHasKey(location, list, ast)) {
             schemaNode_ listNode;
-            listNode.m_prefix = parserContext.m_curModule ? boost::optional<module_>{{*parserContext.m_curModule}} : boost::none;
+            listNode.m_prefix = parserContext.m_curModule.flat_map([] (auto mod) { return boost::optional<module_>{{mod}}; });;
             listNode.m_suffix = list_{parserContext.m_tmpListName};
             location.m_nodes.push_back(listNode);
             parserContext.m_tmpListKeyLeafPath.m_location = location;
-            parserContext.m_tmpListKeyLeafPath.m_node = {boost::none, ast};
+            parserContext.m_tmpListKeyLeafPath.m_node = { parserContext.m_curModule, ast };
         } else {
             parserContext.m_errorMsg = parserContext.m_tmpListName + " is not indexed by \"" + ast + "\".";
             _pass(context) = false;
@@ -577,7 +577,7 @@
     }
 };
 
-std::set<std::string> generateMissingKeyCompletionSet(std::set<std::string> keysNeeded, std::set<std::string> currentSet);
+std::set<std::string> generateMissingKeyCompletionSet(std::set<std::string> keysNeeded, std::map<std::string, leaf_data_> currentSet);
 
 struct createKeySuggestions_class {
     template <typename T, typename Iterator, typename Context>
@@ -593,6 +593,42 @@
     }
 };
 
+std::string leafDataToCompletion(const leaf_data_& value);
+
+struct createValueSuggestions_class {
+    template <typename T, typename Iterator, typename Context>
+    void on_success(Iterator const& begin, Iterator const&, T&, Context const& context)
+    {
+        auto& parserContext = x3::get<parser_context_tag>(context);
+        if (!parserContext.m_completing) {
+            return;
+        }
+        const auto& dataQuery = parserContext.m_dataquery;
+
+        parserContext.m_completionIterator = begin;
+        auto listInstances = dataQuery->listKeys(parserContext.currentDataPath(), {parserContext.m_curModule, parserContext.m_tmpListName});
+
+        decltype(listInstances) filteredInstances;
+        //This filters out instances, which don't correspond to the partial instance we have.
+        const auto partialFitsComplete = [&parserContext] (const auto& complete) {
+            const auto& partial = parserContext.m_tmpListKeys;
+            return std::all_of(partial.begin(), partial.end(), [&complete] (const auto& oneKV) {
+                    const auto& [k, v] = oneKV;
+                    return complete.at(k) == v;
+                    });
+        };
+        std::copy_if(listInstances.begin(), listInstances.end(), std::inserter(filteredInstances, filteredInstances.end()), partialFitsComplete);
+
+        std::set<std::string> validValues;
+
+        std::transform(filteredInstances.begin(), filteredInstances.end(), std::inserter(validValues, validValues.end()), [&parserContext] (const auto& instance) {
+            return leafDataToCompletion(instance.at(parserContext.m_tmpListKeyLeafPath.m_node.second));
+        });
+
+        parserContext.m_suggestions = validValues;
+    }
+};
+
 struct suggestKeysEnd_class {
     template <typename T, typename Iterator, typename Context>
     void on_success(Iterator const& begin, Iterator const&, T&, Context const& context)
diff --git a/src/grammars.hpp b/src/grammars.hpp
index 98ed8b7..11d1db0 100644
--- a/src/grammars.hpp
+++ b/src/grammars.hpp
@@ -71,6 +71,7 @@
 x3::rule<initializePath_class, x3::unused_type> const initializePath = "initializePath";
 x3::rule<createPathSuggestions_class, x3::unused_type> const createPathSuggestions = "createPathSuggestions";
 x3::rule<createKeySuggestions_class, x3::unused_type> const createKeySuggestions = "createKeySuggestions";
+x3::rule<createValueSuggestions_class, x3::unused_type> const createValueSuggestions = "createValueSuggestions";
 x3::rule<suggestKeysEnd_class, x3::unused_type> const suggestKeysEnd = "suggestKeysEnd";
 x3::rule<createCommandSuggestions_class, x3::unused_type> const createCommandSuggestions = "createCommandSuggestions";
 x3::rule<completing_class, x3::unused_type> const completing = "completing";
@@ -110,11 +111,14 @@
 auto const createKeySuggestions_def =
     x3::eps;
 
+auto const createValueSuggestions_def =
+    x3::eps;
+
 auto const suggestKeysEnd_def =
     x3::eps;
 
 auto const keyValue_def =
-    key_identifier > '=' > leaf_data;
+    key_identifier > '=' > createValueSuggestions > leaf_data;
 
 auto const keyValueWrapper =
     lexeme['[' > createKeySuggestions > keyValue > suggestKeysEnd > ']'];
@@ -396,6 +400,7 @@
 BOOST_SPIRIT_DEFINE(command)
 BOOST_SPIRIT_DEFINE(createPathSuggestions)
 BOOST_SPIRIT_DEFINE(createKeySuggestions)
+BOOST_SPIRIT_DEFINE(createValueSuggestions)
 BOOST_SPIRIT_DEFINE(suggestKeysEnd)
 BOOST_SPIRIT_DEFINE(createCommandSuggestions)
 BOOST_SPIRIT_DEFINE(completing)
diff --git a/src/main.cpp b/src/main.cpp
index d1b45e0..fd064ba 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -36,7 +36,8 @@
     std::cout << "Welcome to netconf-cli" << std::endl;
 
     SysrepoAccess datastore("netconf-cli");
-    Parser parser(datastore.schema());
+    auto dataQuery = std::make_shared<DataQuery>(datastore);
+    Parser parser(datastore.schema(), dataQuery);
 
     using replxx::Replxx;
 
diff --git a/src/parser.cpp b/src/parser.cpp
index c3dc088..ff262fc 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -15,8 +15,9 @@
 InvalidCommandException::~InvalidCommandException() = default;
 
 
-Parser::Parser(const std::shared_ptr<const Schema> schema)
+Parser::Parser(const std::shared_ptr<const Schema> schema, const std::shared_ptr<const DataQuery> dataQuery)
     : m_schema(schema)
+    , m_dataquery(dataQuery)
 {
     m_curDir.m_scope = Scope::Absolute;
 }
@@ -29,7 +30,7 @@
 command_ Parser::parseCommand(const std::string& line, std::ostream& errorStream)
 {
     command_ parsedCommand;
-    ParserContext ctx(*m_schema, m_curDir);
+    ParserContext ctx(*m_schema, nullptr, m_curDir);
     auto it = line.begin();
 
     boost::spirit::x3::error_handler<std::string::const_iterator> errorHandler(it, line.end(), errorStream);
@@ -51,7 +52,7 @@
 {
     std::set<std::string> completions;
     command_ parsedCommand;
-    ParserContext ctx(*m_schema, m_curDir);
+    ParserContext ctx(*m_schema, m_dataquery, m_curDir);
     ctx.m_completing = true;
     auto it = line.begin();
     boost::spirit::x3::error_handler<std::string::const_iterator> errorHandler(it, line.end(), errorStream);
diff --git a/src/parser.hpp b/src/parser.hpp
index c2cd8d6..7a94919 100644
--- a/src/parser.hpp
+++ b/src/parser.hpp
@@ -9,6 +9,7 @@
 #pragma once
 #include "ast_commands.hpp"
 #include "ast_path.hpp"
+#include "data_query.hpp"
 #include "schema.hpp"
 
 
@@ -32,7 +33,7 @@
 
 class Parser {
 public:
-    Parser(const std::shared_ptr<const Schema> schema);
+    Parser(const std::shared_ptr<const Schema> schema, const std::shared_ptr<const DataQuery> dataQuery = nullptr);
     command_ parseCommand(const std::string& line, std::ostream& errorStream);
     void changeNode(const dataPath_& name);
     std::string currentNode() const;
@@ -41,5 +42,6 @@
 
 private:
     const std::shared_ptr<const Schema> m_schema;
+    const std::shared_ptr<const DataQuery> m_dataquery;
     dataPath_ m_curDir;
 };
diff --git a/src/parser_context.cpp b/src/parser_context.cpp
index 7750301..26e6fa3 100644
--- a/src/parser_context.cpp
+++ b/src/parser_context.cpp
@@ -7,9 +7,10 @@
 */
 
 #include "parser_context.hpp"
-ParserContext::ParserContext(const Schema& schema, const dataPath_& curDir)
+ParserContext::ParserContext(const Schema& schema, const std::shared_ptr<const DataQuery> dataQuery, const dataPath_& curDir)
     : m_schema(schema)
     , m_curPathOrig(curDir)
+    , m_dataquery(dataQuery)
     , m_curPath(curDir)
 {
     if (!currentDataPath().m_nodes.empty() && currentDataPath().m_nodes.at(0).m_prefix)
diff --git a/src/parser_context.hpp b/src/parser_context.hpp
index 77357e7..98022db 100644
--- a/src/parser_context.hpp
+++ b/src/parser_context.hpp
@@ -6,9 +6,10 @@
  *
 */
 
+#include "data_query.hpp"
 #include "schema.hpp"
 struct ParserContext {
-    ParserContext(const Schema& schema, const dataPath_& curDir);
+    ParserContext(const Schema& schema, const std::shared_ptr<const DataQuery> dataQuery, const dataPath_& curDir);
     schemaPath_ currentSchemaPath();
     dataPath_ currentDataPath();
     void clearPath();
@@ -18,16 +19,17 @@
 
     const Schema& m_schema;
     const dataPath_ m_curPathOrig;
+    const std::shared_ptr<const DataQuery> m_dataquery;
     boost::optional<std::string> m_curModule;
     std::string m_errorMsg;
     std::string m_tmpListName;
     bool m_topLevelModulePresent = false;
-    std::set<std::string> m_tmpListKeys;
 
     struct {
         schemaPath_ m_location;
         ModuleNodePair m_node;
     } m_tmpListKeyLeafPath;
+    std::map<std::string, leaf_data_> m_tmpListKeys;
     bool m_errorHandled = false;
     bool m_completing = false;
     std::set<std::string> m_suggestions;