yang-cli: Allow editing ops data

Change-Id: Ica85cacac7fed0b052687b7262a76411a03e80f1
diff --git a/src/ast_handlers.hpp b/src/ast_handlers.hpp
index 54f3f95..4227097 100644
--- a/src/ast_handlers.hpp
+++ b/src/ast_handlers.hpp
@@ -224,33 +224,6 @@
     }
 };
 
-struct writable_leaf_path_class {
-    template <typename T, typename Iterator, typename Context>
-    void on_success(Iterator const&, Iterator const&, T&, Context const& context)
-    {
-        auto& parserContext = x3::get<parser_context_tag>(context);
-        if (parserContext.currentSchemaPath().m_nodes.empty()) {
-            parserContext.m_errorMsg = "This is not a path to leaf.";
-            _pass(context) = false;
-            return;
-        }
-
-        try {
-            auto lastNode = parserContext.currentSchemaPath().m_nodes.back();
-            auto leaf = std::get<leaf_>(lastNode.m_suffix);
-            auto location = pathWithoutLastNode(parserContext.currentSchemaPath());
-            ModuleNodePair node{lastNode.m_prefix.flat_map([](const auto& it) { return boost::optional<std::string>{it.m_name}; }), leaf.m_name};
-
-            parserContext.m_tmpListKeyLeafPath.m_location = location;
-            parserContext.m_tmpListKeyLeafPath.m_node = node;
-
-        } catch (std::bad_variant_access&) {
-            parserContext.m_errorMsg = "This is not a path to leaf.";
-            _pass(context) = false;
-        }
-    }
-};
-
 struct set_class {
     template <typename Iterator, typename Exception, typename Context>
     x3::error_handler_result on_error(Iterator&, Iterator const&, Exception const& x, Context const& context)
diff --git a/src/ast_path.hpp b/src/ast_path.hpp
index 83727ff..e097a0c 100644
--- a/src/ast_path.hpp
+++ b/src/ast_path.hpp
@@ -135,6 +135,11 @@
     void pushFragment(const dataNode_& fragment);
 };
 
+enum class WritableOps {
+    Yes,
+    No
+};
+
 std::string nodeToSchemaString(decltype(dataPath_::m_nodes)::value_type node);
 
 std::string pathToDataString(const dataPath_& path, Prefixes prefixes);
diff --git a/src/cli.cpp b/src/cli.cpp
index 53670d0..63ef4c3 100644
--- a/src/cli.cpp
+++ b/src/cli.cpp
@@ -36,14 +36,15 @@
   will be used to find a schema for that module.
 
 Usage:
-  yang-cli [-s <search_dir>] [-e enable_features]... [-i data_file]... <schema_file_or_module_name>...
+  yang-cli [--configonly] [-s <search_dir>] [-e enable_features]... [-i data_file]... <schema_file_or_module_name>...
   yang-cli (-h | --help)
   yang-cli --version
 
 Options:
   -s <search_dir>       Set search for schema lookup
   -e <enable_features>  Feature to enable after modules are loaded. This option can be supplied more than once. Format: <module_name>:<feature>
-  -i <data_file>        File to import data from)";
+  -i <data_file>        File to import data from
+  --configonly          Disable editing of operational data)";
 #else
 #error "Unknown CLI backend"
 #endif
@@ -57,6 +58,7 @@
                                true,
                                PROGRAM_NAME " " NETCONF_CLI_VERSION,
                                true);
+    WritableOps writableOps = WritableOps::No;
 
 #if defined(SYSREPO_CLI)
     auto datastoreType = Datastore::Running;
@@ -74,6 +76,12 @@
     std::cout << "Connected to sysrepo [datastore: " << (datastoreType == Datastore::Startup ? "startup" : "running") << "]" << std::endl;
 #elif defined(YANG_CLI)
     YangAccess datastore;
+    if (args["--configonly"].asBool()) {
+        writableOps = WritableOps::No;
+    } else {
+        writableOps = WritableOps::Yes;
+        std::cout << "ops is writable" << std::endl;
+    }
     if (const auto& search_dir = args["-s"]) {
         datastore.addSchemaDir(search_dir.asString());
     }
@@ -116,7 +124,7 @@
 #endif
 
     auto dataQuery = std::make_shared<DataQuery>(datastore);
-    Parser parser(datastore.schema(), dataQuery);
+    Parser parser(datastore.schema(), writableOps, dataQuery);
 
     using replxx::Replxx;
 
diff --git a/src/parser.cpp b/src/parser.cpp
index bf00a7e..b5878bf 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -14,10 +14,11 @@
 
 InvalidCommandException::~InvalidCommandException() = default;
 
-Parser::Parser(const std::shared_ptr<const Schema> schema, const std::shared_ptr<const DataQuery> dataQuery)
+Parser::Parser(const std::shared_ptr<const Schema> schema, WritableOps writableOps, const std::shared_ptr<const DataQuery> dataQuery)
     : m_schema(schema)
     , m_dataquery(dataQuery)
     , m_curDir({Scope::Absolute, {}})
+    , m_writableOps(writableOps)
 {
 }
 
@@ -36,7 +37,8 @@
 
     auto grammar =
             x3::with<parser_context_tag>(ctx)[
-            x3::with<x3::error_handler_tag>(std::ref(errorHandler))[command]
+            x3::with<x3::error_handler_tag>(std::ref(errorHandler))[
+            x3::with<writableOps_tag>(m_writableOps)[command]]
     ];
     bool result = x3::phrase_parse(it, line.end(), grammar, x3::space, parsedCommand);
 
@@ -58,7 +60,8 @@
 
     auto grammar =
             x3::with<parser_context_tag>(ctx)[
-            x3::with<x3::error_handler_tag>(std::ref(errorHandler))[command]
+            x3::with<x3::error_handler_tag>(std::ref(errorHandler))[
+            x3::with<writableOps_tag>(m_writableOps)[command]]
     ];
     x3::phrase_parse(it, line.end(), grammar, x3::space, parsedCommand);
 
diff --git a/src/parser.hpp b/src/parser.hpp
index 14daa9e..1e8cdbb 100644
--- a/src/parser.hpp
+++ b/src/parser.hpp
@@ -33,7 +33,7 @@
 
 class Parser {
 public:
-    Parser(const std::shared_ptr<const Schema> schema, const std::shared_ptr<const DataQuery> dataQuery = nullptr);
+    Parser(const std::shared_ptr<const Schema> schema, WritableOps writableOps = WritableOps::No, 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;
@@ -44,4 +44,5 @@
     const std::shared_ptr<const Schema> m_schema;
     const std::shared_ptr<const DataQuery> m_dataquery;
     dataPath_ m_curDir;
+    const WritableOps m_writableOps;
 };
diff --git a/src/path_parser.hpp b/src/path_parser.hpp
index c89b9df..39e2797 100644
--- a/src/path_parser.hpp
+++ b/src/path_parser.hpp
@@ -14,7 +14,6 @@
 
 namespace x3 = boost::spirit::x3;
 
-x3::rule<writable_leaf_path_class, dataPath_> const writableLeafPath = "writableLeafPath";
 x3::rule<presenceContainerPath_class, dataPath_> const presenceContainerPath = "presenceContainerPath";
 x3::rule<listInstancePath_class, dataPath_> const listInstancePath = "listInstancePath";
 x3::rule<leafListElementPath_class, dataPath_> const leafListElementPath = "leafListElementPath";
@@ -389,6 +388,36 @@
     return schema.isConfig(path);
 };
 
+struct writableOps_tag;
+
+PathParser<PathParserMode::DataPath, CompletionMode::Data> const dataPathFilterConfigFalse{filterConfigFalse};
+
+struct WritableLeafPath : x3::parser<WritableLeafPath> {
+    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;
+        if (x3::get<writableOps_tag>(ctx) == WritableOps::Yes) {
+            res = dataPath.parse(begin, end, ctx, rctx, attr);
+        } else {
+            res = dataPathFilterConfigFalse.parse(begin, end, ctx, rctx, attr);
+        }
+        if (!res) {
+            return false;
+        }
+
+        if (attr.m_nodes.empty() || !std::holds_alternative<leaf_>(attr.m_nodes.back().m_suffix)) {
+            auto& parserContext = x3::get<parser_context_tag>(ctx);
+            parserContext.m_errorMsg = "This is not a path to leaf.";
+            return false;
+        }
+
+        return true;
+    }
+
+} writableLeafPath;
+
 auto const writableLeafPath_def =
     PathParser<PathParserMode::DataPath, CompletionMode::Data>{filterConfigFalse};
 
@@ -414,7 +443,6 @@
 BOOST_SPIRIT_DEFINE(keyValue)
 BOOST_SPIRIT_DEFINE(key_identifier)
 BOOST_SPIRIT_DEFINE(listSuffix)
-BOOST_SPIRIT_DEFINE(writableLeafPath)
 BOOST_SPIRIT_DEFINE(presenceContainerPath)
 BOOST_SPIRIT_DEFINE(listInstancePath)
 BOOST_SPIRIT_DEFINE(leafListElementPath)