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/proxy_datastore.cpp b/src/proxy_datastore.cpp
index ab9193c..e6878cb 100644
--- a/src/proxy_datastore.cpp
+++ b/src/proxy_datastore.cpp
@@ -4,36 +4,39 @@
  * Written by Václav Kubernát <kubernat@cesnet.cz>
  *
 */
+#include <boost/algorithm/string/predicate.hpp>
 #include "proxy_datastore.hpp"
+#include "yang_schema.hpp"
 
-ProxyDatastore::ProxyDatastore(const std::shared_ptr<DatastoreAccess>& datastore)
+ProxyDatastore::ProxyDatastore(const std::shared_ptr<DatastoreAccess>& datastore, std::function<std::shared_ptr<DatastoreAccess>(const std::shared_ptr<DatastoreAccess>&)> createTemporaryDatastore)
     : m_datastore(datastore)
+    , m_createTemporaryDatastore(createTemporaryDatastore)
 {
 }
 
 DatastoreAccess::Tree ProxyDatastore::getItems(const std::string& path) const
 {
-    return m_datastore->getItems(path);
+    return pickDatastore(path)->getItems(path);
 }
 
 void ProxyDatastore::setLeaf(const std::string& path, leaf_data_ value)
 {
-    m_datastore->setLeaf(path, value);
+    pickDatastore(path)->setLeaf(path, value);
 }
 
 void ProxyDatastore::createItem(const std::string& path)
 {
-    m_datastore->createItem(path);
+    pickDatastore(path)->createItem(path);
 }
 
 void ProxyDatastore::deleteItem(const std::string& path)
 {
-    m_datastore->deleteItem(path);
+    pickDatastore(path)->deleteItem(path);
 }
 
 void ProxyDatastore::moveItem(const std::string& source, std::variant<yang::move::Absolute, yang::move::Relative> move)
 {
-    m_datastore->moveItem(source, move);
+    pickDatastore(source)->moveItem(source, move);
 }
 
 void ProxyDatastore::commitChanges()
@@ -56,7 +59,41 @@
     return m_datastore->dump(format);
 }
 
+void ProxyDatastore::initiateRpc(const std::string& rpcPath)
+{
+    if (m_inputDatastore) {
+        throw std::runtime_error("RPC input already in progress (" + m_rpcPath + ")");
+    }
+    m_inputDatastore = m_createTemporaryDatastore(m_datastore);
+    m_rpcPath = rpcPath;
+    m_inputDatastore->createItem(rpcPath);
+}
+
+DatastoreAccess::Tree ProxyDatastore::executeRpc()
+{
+    if (!m_inputDatastore) {
+        throw std::runtime_error("No RPC input in progress");
+    }
+    auto inputData = m_inputDatastore->getItems("/");
+    m_inputDatastore = nullptr;
+    return m_datastore->executeRpc(m_rpcPath, inputData);
+}
+
+void ProxyDatastore::cancelRpc()
+{
+    m_inputDatastore = nullptr;
+}
+
 std::shared_ptr<Schema> ProxyDatastore::schema() const
 {
     return m_datastore->schema();
 }
+
+std::shared_ptr<DatastoreAccess> ProxyDatastore::pickDatastore(const std::string& path) const
+{
+    if (!m_inputDatastore || !boost::starts_with(path, m_rpcPath)) {
+        return m_datastore;
+    } else {
+        return m_inputDatastore;
+    }
+}