Add support for executing RPCs

Creating a temporary YangAccess for RPC input means I need to somehow
give the right libyang schemas. For that reason I supply a callable
which is able to fetch the schema and create a YangAccess instance for
ProxyDatastore.

The ProxyDatastore class now has a simple mechanism for deciding whether
to use the normal datastore and the temporary based on a path prefix.

Change-Id: Ib455f53237598bc2620161a44fb89c48ddfeb6e3
diff --git a/src/interpreter.cpp b/src/interpreter.cpp
index 5ff73e7..26e2be4 100644
--- a/src/interpreter.cpp
+++ b/src/interpreter.cpp
@@ -30,6 +30,26 @@
     }
 };
 
+namespace {
+void printTree(const DatastoreAccess::Tree tree)
+{
+    for (auto it = tree.begin(); it != tree.end(); it++) {
+        auto [path, value] = *it;
+        if (value.type() == typeid(special_) && boost::get<special_>(value).m_value == SpecialValue::LeafList) {
+            auto leafListPrefix = path;
+            std::cout << path << " = " << leafDataToString(value) << std::endl;
+
+            while (it + 1 != tree.end() && boost::starts_with((it + 1)->first, leafListPrefix)) {
+                ++it;
+                std::cout << stripLeafListValueFromPath(it->first) << " = " << leafDataToString(it->second) << std::endl;
+            }
+        } else {
+            std::cout << path << " = " << leafDataToString(value) << std::endl;
+        }
+    }
+}
+}
+
 template <typename PathType>
 std::string pathToString(const PathType& path)
 {
@@ -64,20 +84,7 @@
 void Interpreter::operator()(const get_& get) const
 {
     auto items = m_datastore.getItems(pathToString(toCanonicalPath(get.m_path)));
-    for (auto it = items.begin(); it != items.end(); it++) {
-        auto [path, value] = *it;
-        if (value.type() == typeid(special_) && boost::get<special_>(value).m_value == SpecialValue::LeafList) {
-            auto leafListPrefix = path;
-            std::cout << path << " = " << leafDataToString(value) << std::endl;
-
-            while (it + 1 != items.end() && boost::starts_with((it + 1) ->first, leafListPrefix)) {
-                ++it;
-                std::cout << stripLeafListValueFromPath(it->first) << " = " << leafDataToString(it->second) << std::endl;
-            }
-        } else {
-            std::cout << path << " = " << leafDataToString(value) << std::endl;
-        }
-    }
+    printTree(items);
 }
 
 void Interpreter::operator()(const cd_& cd) const
@@ -162,12 +169,14 @@
     case yang::NodeTypes::List:
         ss << "list";
         break;
+    case yang::NodeTypes::Rpc:
+        ss << "RPC";
+        break;
     case yang::NodeTypes::Action:
     case yang::NodeTypes::AnyXml:
     case yang::NodeTypes::LeafList:
     case yang::NodeTypes::Notification:
-    case yang::NodeTypes::Rpc:
-        throw std::logic_error("describe got an rpc or an action: this should never happen, because their paths cannot be parsed");
+        throw std::logic_error("describe can't handle the type of " + path);
     }
 
     if (!m_datastore.schema()->isConfig(path)) {
@@ -201,6 +210,26 @@
     std::cout << m_datastore.dump(dump.m_format) << "\n";
 }
 
+void Interpreter::operator()(const rpc_& rpc) const
+{
+    m_datastore.initiateRpc(pathToString(toCanonicalPath(rpc.m_path)));
+    m_parser.changeNode(rpc.m_path);
+}
+
+void Interpreter::operator()(const exec_&) const
+{
+    m_parser.changeNode({Scope::Absolute, {}});
+    auto output = m_datastore.executeRpc();
+    std::cout << "RPC output:\n";
+    printTree(output);
+}
+
+void Interpreter::operator()(const cancel_&) const
+{
+    m_parser.changeNode({Scope::Absolute, {}});
+    m_datastore.cancelRpc();
+}
+
 struct commandLongHelpVisitor : boost::static_visitor<const char*> {
     template <typename T>
     auto constexpr operator()(boost::type<T>) const