Allow immediately executing RPC with no input
Story: https://tree.taiga.io/project/jktjkt-netconf-cli/us/189
Change-Id: I876437b798e995c4484e97b409117a2bb207e864
diff --git a/src/ast_commands.cpp b/src/ast_commands.cpp
index 5b99c6a..fa2b56c 100644
--- a/src/ast_commands.cpp
+++ b/src/ast_commands.cpp
@@ -51,3 +51,8 @@
{
return this->m_path == other.m_path;
}
+
+bool exec_::operator==(const exec_& other) const
+{
+ return this->m_path == other.m_path;
+}
diff --git a/src/ast_commands.hpp b/src/ast_commands.hpp
index db78611..9ec2740 100644
--- a/src/ast_commands.hpp
+++ b/src/ast_commands.hpp
@@ -258,14 +258,20 @@
static constexpr auto name = "exec";
static constexpr auto shortHelp = "exec - Execute RPC/action.";
static constexpr auto longHelp = R"(
- exec
+ exec [path]
This command executes the RPC/action you have previously initiated via the
`prepare` command.
+ If the RPC/action has no input parameters, it can be directly execute via
+ `exec` without usgin `prepare`.
+
Usage:
- /> exec)";
+ /> exec
+ /> exec /mod:myRpc)";
bool operator==(const exec_& other) const;
+
+ boost::optional<dataPath_> m_path;
};
struct cancel_ : x3::position_tagged {
@@ -332,3 +338,4 @@
BOOST_FUSION_ADAPT_STRUCT(move_, m_source, m_destination)
BOOST_FUSION_ADAPT_STRUCT(dump_, m_format)
BOOST_FUSION_ADAPT_STRUCT(prepare_, m_path)
+BOOST_FUSION_ADAPT_STRUCT(exec_, m_path)
diff --git a/src/grammars.hpp b/src/grammars.hpp
index 02da179..2ff6e7a 100644
--- a/src/grammars.hpp
+++ b/src/grammars.hpp
@@ -259,10 +259,10 @@
} const dump_args;
auto const prepare_def =
- prepare_::name > space_separator > rpcActionPath;
+ prepare_::name > space_separator > as<dataPath_>[RpcActionPath<AllowInput::Yes>{}];
auto const exec_def =
- exec_::name >> x3::attr(exec_{});
+ exec_::name > -(space_separator > as<dataPath_>[RpcActionPath<AllowInput::No>{}]);
auto const cancel_def =
cancel_::name >> x3::attr(cancel_{});
diff --git a/src/interpreter.cpp b/src/interpreter.cpp
index acb3f0b..ac958dc 100644
--- a/src/interpreter.cpp
+++ b/src/interpreter.cpp
@@ -220,9 +220,12 @@
m_parser.changeNode(prepare.m_path);
}
-void Interpreter::operator()(const exec_&) const
+void Interpreter::operator()(const exec_& exec) const
{
m_parser.changeNode({Scope::Absolute, {}});
+ if (exec.m_path) {
+ m_datastore.initiate(pathToString(toCanonicalPath(*exec.m_path)));
+ }
auto output = m_datastore.execute();
std::cout << "RPC/action output:\n";
printTree(output);
diff --git a/src/path_parser.hpp b/src/path_parser.hpp
index 58d174e..c3df931 100644
--- a/src/path_parser.hpp
+++ b/src/path_parser.hpp
@@ -428,12 +428,28 @@
} const writableLeafPath;
-struct RpcActionPath : x3::parser<RpcActionPath> {
+enum class AllowInput {
+ Yes,
+ No
+};
+
+template <AllowInput ALLOW_INPUT>
+struct RpcActionPath : x3::parser<RpcActionPath<ALLOW_INPUT>> {
using attribute_type = dataPath_;
template <typename It, typename Ctx, typename RCtx, typename Attr>
static bool parse(It& begin, It end, Ctx const& ctx, RCtx& rctx, Attr& attr)
{
- bool res = dataPath.parse(begin, end, ctx, rctx, attr);
+ auto grammar = PathParser<PathParserMode::DataPath, CompletionMode::Data>{[] (const Schema& schema, const std::string& path) {
+ if constexpr (ALLOW_INPUT == AllowInput::No) {
+ auto nodeType = schema.nodeType(path);
+ if (nodeType == yang::NodeTypes::Rpc || nodeType == yang::NodeTypes::Action) {
+ return !schema.hasInputNodes(path);
+ }
+ }
+
+ return true;
+ }};
+ bool res = grammar.parse(begin, end, ctx, rctx, attr);
if (!res) {
return false;
}
@@ -449,8 +465,6 @@
}
};
-auto const rpcActionPath = as<dataPath_>[RpcActionPath()];
-
auto const noRpcOrAction = [](const Schema& schema, const std::string& path) {
auto nodeType = schema.nodeType(path);
return nodeType != yang::NodeTypes::Rpc && nodeType != yang::NodeTypes::Action;
diff --git a/src/schema.hpp b/src/schema.hpp
index 00586b5..6955686 100644
--- a/src/schema.hpp
+++ b/src/schema.hpp
@@ -68,6 +68,7 @@
[[nodiscard]] virtual std::string leafrefPath(const std::string& leafrefPath) const = 0;
[[nodiscard]] virtual std::optional<std::string> description(const std::string& location) const = 0;
[[nodiscard]] virtual yang::Status status(const std::string& location) const = 0;
+ [[nodiscard]] virtual bool hasInputNodes(const std::string& path) const = 0;
[[nodiscard]] virtual std::set<ModuleNodePair> availableNodes(const boost::variant<dataPath_, schemaPath_, module_>& path, const Recursion recursion) const = 0;
};
diff --git a/src/static_schema.cpp b/src/static_schema.cpp
index a75f471..c1d4417 100644
--- a/src/static_schema.cpp
+++ b/src/static_schema.cpp
@@ -41,6 +41,8 @@
//create a new set of children for the new node
std::string key = joinPaths(location, name);
m_nodes.emplace(key, std::unordered_map<std::string, NodeInfo>());
+ m_nodes.emplace(joinPaths(key, "input"), std::unordered_map<std::string, NodeInfo>());
+ m_nodes.emplace(joinPaths(key, "output"), std::unordered_map<std::string, NodeInfo>());
}
void StaticSchema::addAction(const std::string& location, const std::string& name)
@@ -272,6 +274,15 @@
throw std::runtime_error{"Internal error: StaticSchema::status(std::string) not implemented. The tests should not have called this overload."};
}
+bool StaticSchema::hasInputNodes(const std::string& path) const
+{
+ if (nodeType(path) != yang::NodeTypes::Action && nodeType(path) != yang::NodeTypes::Rpc) {
+ throw std::logic_error("StaticSchema::hasInputNodes called with non-RPC/action path");
+ }
+
+ return m_nodes.at(joinPaths(path, "input")).size() != 0;
+}
+
yang::NodeTypes StaticSchema::nodeType(const std::string& path) const
{
auto locationString = stripLastNodeFromPath(path);
diff --git a/src/static_schema.hpp b/src/static_schema.hpp
index 3009d7e..0b9e59e 100644
--- a/src/static_schema.hpp
+++ b/src/static_schema.hpp
@@ -80,6 +80,7 @@
std::set<ModuleNodePair> availableNodes(const boost::variant<dataPath_, schemaPath_, module_>& path, const Recursion recursion) const override;
std::optional<std::string> description(const std::string& path) const override;
yang::Status status(const std::string& location) const override;
+ bool hasInputNodes(const std::string& path) const override;
/** A helper for making tests a little bit easier. It returns all
* identities which are based on the argument passed and which can then be
diff --git a/src/yang_schema.cpp b/src/yang_schema.cpp
index 7f27711..d6c0bd8 100644
--- a/src/yang_schema.cpp
+++ b/src/yang_schema.cpp
@@ -485,6 +485,17 @@
}
}
+bool YangSchema::hasInputNodes(const std::string& path) const
+{
+ auto node = getSchemaNode(path.c_str());
+ if (auto type = node->nodetype(); type != LYS_ACTION && type != LYS_RPC) {
+ throw std::logic_error("StaticSchema::hasInputNodes called with non-RPC/action path");
+ }
+
+ // The first child gives the /input node and then I check whether it has a child.
+ return node->child()->child().get();
+}
+
bool YangSchema::isConfig(const std::string& path) const
{
auto node = getSchemaNode(path.c_str());
diff --git a/src/yang_schema.hpp b/src/yang_schema.hpp
index 632f538..dc45097 100644
--- a/src/yang_schema.hpp
+++ b/src/yang_schema.hpp
@@ -47,6 +47,7 @@
[[nodiscard]] std::set<ModuleNodePair> availableNodes(const boost::variant<dataPath_, schemaPath_, module_>& path, const Recursion recursion) const override;
[[nodiscard]] std::optional<std::string> description(const std::string& path) const override;
[[nodiscard]] yang::Status status(const std::string& location) const override;
+ [[nodiscard]] bool hasInputNodes(const std::string& path) const override;
void registerModuleCallback(const std::function<std::string(const char*, const char*, const char*, const char*)>& clb);