Port to libyang-cpp and sysrepo-cpp API change to std::string

Change-Id: I9e683e0ab3a5db696c35699d22cf1cb982c8ceda
Depends-on: https://gerrit.cesnet.cz/c/CzechLight/dependencies/+/5564
diff --git a/src/libyang_utils.cpp b/src/libyang_utils.cpp
index f3881fe..b09454e 100644
--- a/src/libyang_utils.cpp
+++ b/src/libyang_utils.cpp
@@ -123,9 +123,9 @@
 
 libyang::DataNode treeToRpcInput(libyang::Context ctx, const std::string& path, DatastoreAccess::Tree in)
 {
-    auto root = ctx.newPath(path.c_str(), nullptr, libyang::CreationOptions::Update);
+    auto root = ctx.newPath(path, std::nullopt, libyang::CreationOptions::Update);
     for (const auto& [k, v] : in) {
-        root.newPath(k.c_str(), leafDataToString(v).c_str(), libyang::CreationOptions::Update);
+        root.newPath(k, leafDataToString(v), libyang::CreationOptions::Update);
     }
 
     return root;
diff --git a/src/netconf_access.cpp b/src/netconf_access.cpp
index 0c25cc8..a3d30ba 100644
--- a/src/netconf_access.cpp
+++ b/src/netconf_access.cpp
@@ -59,7 +59,7 @@
 }
 
 NetconfAccess::NetconfAccess(const std::string& hostname, uint16_t port, const std::string& user, const std::string& pubKey, const std::string& privKey)
-    : m_context(nullptr, libyang::ContextOptions::SetPrivParsed)
+    : m_context(std::nullopt, libyang::ContextOptions::SetPrivParsed)
     , m_session(libnetconf::client::Session::connectPubkey(hostname, port, user, pubKey, privKey, m_context))
     , m_schema(std::make_shared<YangSchema>(m_context))
 {
@@ -67,7 +67,7 @@
 }
 
 NetconfAccess::NetconfAccess(const int source, const int sink)
-    : m_context(nullptr, libyang::ContextOptions::SetPrivParsed)
+    : m_context(std::nullopt, libyang::ContextOptions::SetPrivParsed)
     , m_session(libnetconf::client::Session::connectFd(source, sink, m_context))
     , m_schema(std::make_shared<YangSchema>(m_context))
 {
@@ -82,7 +82,7 @@
 }
 
 NetconfAccess::NetconfAccess(const std::string& socketPath)
-    : m_context(nullptr, libyang::ContextOptions::SetPrivParsed)
+    : m_context(std::nullopt, libyang::ContextOptions::SetPrivParsed)
     , m_session(libnetconf::client::Session::connectSocket(socketPath, m_context))
     , m_schema(std::make_shared<YangSchema>(m_context))
 {
@@ -151,16 +151,16 @@
 void NetconfAccess::moveItem(const std::string& source, std::variant<yang::move::Absolute, yang::move::Relative> move)
 {
     auto nodes = m_schema->dataNodeFromPath(source);
-    auto sourceNode = *(nodes.createdNode->findPath(source.c_str()));
+    auto sourceNode = *(nodes.createdNode->findPath(source));
     auto yangModule = *m_schema->getYangModule("yang");
-    sourceNode.newMeta(yangModule, "insert", toYangInsert(move).c_str());
+    sourceNode.newMeta(yangModule, "insert", toYangInsert(move));
 
     if (std::holds_alternative<yang::move::Relative>(move)) {
         auto relative = std::get<yang::move::Relative>(move);
         if (m_schema->nodeType(source) == yang::NodeTypes::LeafList) {
-            sourceNode.newMeta(yangModule, "value", leafDataToString(relative.m_path.at(".")).c_str());
+            sourceNode.newMeta(yangModule, "value", leafDataToString(relative.m_path.at(".")));
         } else {
-            sourceNode.newMeta(yangModule, "key", instanceToString(relative.m_path, std::string{nodes.createdNode->schema().module().name()}).c_str());
+            sourceNode.newMeta(yangModule, "key", instanceToString(relative.m_path, std::string{nodes.createdNode->schema().module().name()}));
         }
     }
     doEditFromDataNode(sourceNode);
@@ -222,15 +222,15 @@
 std::vector<ListInstance> NetconfAccess::listInstances(const std::string& path)
 {
     std::vector<ListInstance> res;
-    auto keys = m_session->libyangContext().findXPath(path.c_str()).front().asList().keys();
-    auto nodes = m_session->libyangContext().newPath2(path.c_str(), nullptr, libyang::CreationOptions::Opaque);
+    auto keys = m_session->libyangContext().findXPath(path).front().asList().keys();
+    auto nodes = m_session->libyangContext().newPath2(path, std::nullopt, libyang::CreationOptions::Opaque);
 
     // Here we create a tree with "selection leafs" for all they keys of our wanted list. These leafs tell NETCONF, that
     // we only want the list's keys and not any other data.
     for (const auto& keyLeaf : keys) {
         // Selection leafs need to be inserted directly to the list using relative paths, that's why `newNode` is used
         // here.
-        nodes.createdNode->newPath(keyLeaf.name().data(), nullptr, libyang::CreationOptions::Opaque);
+        nodes.createdNode->newPath(keyLeaf.name().data(), std::nullopt, libyang::CreationOptions::Opaque);
     }
 
     // Have to use `newParent` in case our wanted list is a nested list. With `newNode` I would only send the inner
@@ -241,7 +241,7 @@
         return res;
     }
 
-    for (const auto& instance : instances->findXPath(path.c_str())) {
+    for (const auto& instance : instances->findXPath(path)) {
         ListInstance instanceRes;
 
         for (const auto& keyLeaf : instance.child()->siblings()) {
diff --git a/src/sysrepo_access.cpp b/src/sysrepo_access.cpp
index 34d255a..34e0388 100644
--- a/src/sysrepo_access.cpp
+++ b/src/sysrepo_access.cpp
@@ -74,7 +74,7 @@
 
     try {
         m_session.switchDatastore(targetToDs_get(m_target));
-        auto config = m_session.getData(((path == "/") ? "/*" : path).c_str());
+        auto config = m_session.getData((path == "/") ? "/*" : path);
         if (config) {
             lyNodesToTree(res, config->siblings());
         }
@@ -89,7 +89,7 @@
     try {
         m_session.switchDatastore(targetToDs_set(m_target));
         auto lyValue = value.type() == typeid(empty_) ? "" : leafDataToString(value);
-        m_session.setItem(path.c_str(), lyValue.c_str(), sysrepo::EditOptions::Isolate);
+        m_session.setItem(path, lyValue, sysrepo::EditOptions::Isolate);
     } catch (sysrepo::Error& ex) {
         reportErrors();
     }
@@ -99,7 +99,7 @@
 {
     try {
         m_session.switchDatastore(targetToDs_set(m_target));
-        m_session.setItem(path.c_str(), nullptr);
+        m_session.setItem(path, std::nullopt);
     } catch (sysrepo::Error& ex) {
         reportErrors();
     }
@@ -112,7 +112,7 @@
         // not supported.
         // https://github.com/sysrepo/sysrepo/issues/1967#issuecomment-625085090
         m_session.switchDatastore(targetToDs_set(m_target));
-        m_session.deleteItem(path.c_str(), sysrepo::EditOptions::Isolate);
+        m_session.deleteItem(path, sysrepo::EditOptions::Isolate);
     } catch (sysrepo::Error& ex) {
         reportErrors();
     }
@@ -146,7 +146,7 @@
         }
     }
     m_session.switchDatastore(targetToDs_set(m_target));
-    m_session.moveItem(source.c_str(), toSrMoveOp(move), destination.c_str());
+    m_session.moveItem(source, toSrMoveOp(move), destination);
 }
 
 void SysrepoAccess::commitChanges()
@@ -180,7 +180,7 @@
 void SysrepoAccess::copyConfig(const Datastore source, const Datastore destination)
 {
     m_session.switchDatastore(toSrDatastore(destination));
-    m_session.copyConfig(toSrDatastore(source), nullptr, OPERATION_TIMEOUT_MS);
+    m_session.copyConfig(toSrDatastore(source), std::nullopt, OPERATION_TIMEOUT_MS);
 }
 
 std::shared_ptr<Schema> SysrepoAccess::schema()
@@ -206,12 +206,12 @@
 std::vector<ListInstance> SysrepoAccess::listInstances(const std::string& path)
 {
     std::vector<ListInstance> res;
-    auto lists = m_session.getData(path.c_str());
+    auto lists = m_session.getData(path);
     if (!lists) {
         return res;
     }
 
-    auto instances = lists->findXPath(path.c_str());
+    auto instances = lists->findXPath(path);
     if (instances.empty()) {
         return res;
     }
diff --git a/src/yang_access.cpp b/src/yang_access.cpp
index 7fd9010..6c18779 100644
--- a/src/yang_access.cpp
+++ b/src/yang_access.cpp
@@ -16,7 +16,7 @@
 }
 
 YangAccess::YangAccess()
-    : m_ctx(nullptr, libyang::ContextOptions::DisableSearchCwd | libyang::ContextOptions::SetPrivParsed)
+    : m_ctx(std::nullopt, libyang::ContextOptions::DisableSearchCwd | libyang::ContextOptions::SetPrivParsed)
     , m_datastore(std::nullopt)
     , m_schema(std::make_shared<YangSchema>(m_ctx))
 {
@@ -45,9 +45,9 @@
 {
     try {
         if (m_datastore) {
-            m_datastore->newPath(path.c_str(), value ? value->c_str() : nullptr, libyang::CreationOptions::Update);
+            m_datastore->newPath(path, value, libyang::CreationOptions::Update);
         } else {
-            m_datastore = m_ctx.newPath(path.c_str(), value ? value->c_str() : nullptr, libyang::CreationOptions::Update);
+            m_datastore = m_ctx.newPath(path, value, libyang::CreationOptions::Update);
         }
     } catch (libyang::Error&) {
         getErrorsAndThrow();
@@ -82,7 +82,7 @@
         // Otherwise the datastore just doesn't contain the wanted node.
         throw DatastoreException{{DatastoreError{"Datastore is empty.", path}}};
     }
-    auto toRemove = m_datastore->findPath(path.c_str());
+    auto toRemove = m_datastore->findPath(path);
     if (!toRemove) {
         // Otherwise the datastore just doesn't contain the wanted node.
         throw DatastoreException{{DatastoreError{"Data node doesn't exist.", path}}};
@@ -105,7 +105,7 @@
         return res;
     }
 
-    auto set = m_datastore->findXPath(path == "/" ? "/*" : path.c_str());
+    auto set = m_datastore->findXPath(path == "/" ? "/*" : path);
 
     lyNodesToTree(res, set);
     return res;
@@ -134,7 +134,7 @@
 
     void operator()(yang::move::Absolute absolute) const
     {
-        auto set = m_sourceNode.findXPath(m_sourceNode.schema().path().get().get());
+        auto set = m_sourceNode.findXPath(m_sourceNode.schema().path());
         if (set.size() == 1) { // m_sourceNode is the sole instance, do nothing
             return;
         }
@@ -160,7 +160,7 @@
     {
         auto keySuffix = m_sourceNode.schema().nodeType() == libyang::NodeType::List ? instanceToString(relative.m_path)
                                                                     : leafDataToString(relative.m_path.at("."));
-        auto destNode = m_sourceNode.findSiblingVal(m_sourceNode.schema(), keySuffix.c_str());
+        auto destNode = m_sourceNode.findSiblingVal(m_sourceNode.schema(), keySuffix);
 
         if (relative.m_position == yang::move::Relative::Position::After) {
             destNode->insertAfter(m_sourceNode);
@@ -177,7 +177,7 @@
         throw DatastoreException{{DatastoreError{"Datastore is empty.", source}}};
     }
 
-    auto sourceNode = m_datastore->findPath(source.c_str());
+    auto sourceNode = m_datastore->findPath(source);
 
     if (!sourceNode) {
         // The datastore doesn't contain the wanted node.
@@ -199,7 +199,7 @@
 {
     auto root = [&path, this]  {
         try {
-            return m_ctx.newPath(path.c_str());
+            return m_ctx.newPath(path);
         } catch (libyang::ErrorWithCode& err) {
             getErrorsAndThrow();
         }
@@ -211,7 +211,7 @@
         }
 
         try {
-            root.newPath(k.c_str(), leafDataToString(v).c_str(), libyang::CreationOptions::Update);
+            root.newPath(k, leafDataToString(v), libyang::CreationOptions::Update);
         } catch (libyang::ErrorWithCode& err) {
             getErrorsAndThrow();
         }
@@ -238,7 +238,7 @@
         return res;
     }
 
-    auto instances = m_datastore->findXPath(path.c_str());
+    auto instances = m_datastore->findXPath(path);
     for (const auto& list : instances) {
         ListInstance instance;
         for (const auto& child : list.child()->siblings()) {
@@ -297,7 +297,7 @@
     std::cout << "Parsing \"" << path << "\" as " << (firstChar == '{' ? "JSON" : "XML") << "...\n";
 
     auto dataNode = m_ctx.parseDataPath(
-            path.c_str(),
+            path,
             firstChar == '{' ? libyang::DataFormat::JSON : libyang::DataFormat::XML,
             strict == StrictDataParsing::Yes ? std::optional{libyang::ParseOptions::Strict} : std::nullopt,
             libyang::ValidationOptions::Present);
diff --git a/src/yang_schema.cpp b/src/yang_schema.cpp
index 1c2df80..5a31a49 100644
--- a/src/yang_schema.cpp
+++ b/src/yang_schema.cpp
@@ -32,7 +32,7 @@
 };
 
 YangSchema::YangSchema()
-    : m_context(nullptr, libyang::ContextOptions::DisableSearchDirs | libyang::ContextOptions::SetPrivParsed)
+    : m_context(std::nullopt, libyang::ContextOptions::DisableSearchDirs | libyang::ContextOptions::SetPrivParsed)
 {
 }
 
@@ -60,7 +60,7 @@
 
 bool YangSchema::isModule(const std::string& name) const
 {
-    return m_context.getModuleImplemented(name.c_str()).has_value();
+    return m_context.getModuleImplemented(name).has_value();
 }
 
 bool YangSchema::listHasKey(const schemaPath_& listPath, const std::string& key) const
@@ -86,14 +86,14 @@
     //
     // Also, we need to use findPath twice if we're trying to find output nodes.
     try {
-        return m_context.findPath(node.c_str());
+        return m_context.findPath(node);
     } catch (libyang::ErrorWithCode& err) {
         if (err.code() != libyang::ErrorCode::ValidationFailure) {
             throw;
         }
     }
     try {
-        return m_context.findPath(node.c_str(), libyang::OutputNodes::Yes);
+        return m_context.findPath(node, libyang::OutputNodes::Yes);
     } catch (libyang::ErrorWithCode& err) {
         if (err.code() != libyang::ErrorCode::ValidationFailure) {
             throw;
@@ -325,7 +325,7 @@
     std::string topLevelModule;
 
     if (path.type() == typeid(module_)) {
-        nodeCollections.emplace_back(m_context.getModule(boost::get<module_>(path).m_name.c_str())->childInstantiables());
+        nodeCollections.emplace_back(m_context.getModule(boost::get<module_>(path).m_name)->childInstantiables());
     } else {
         auto schemaPath = anyPathToSchemaPath(path);
         if (schemaPath.m_nodes.empty()) {
@@ -372,7 +372,7 @@
 
 void YangSchema::loadModule(const std::string& moduleName)
 {
-    m_context.loadModule(moduleName.c_str());
+    m_context.loadModule(moduleName);
 }
 
 void YangSchema::setEnabledFeatures(const std::string& moduleName, const std::vector<std::string>& features)
@@ -389,16 +389,16 @@
     }
 }
 
-void YangSchema::registerModuleCallback(const std::function<std::string(const char*, const char*, const char*, const char*)>& clb)
+void YangSchema::registerModuleCallback(const std::function<std::string(const std::string_view, const std::optional<std::string_view>, const std::optional<std::string_view>, const std::optional<std::string_view>)>& clb)
 {
-    auto lambda = [clb](const char* mod_name, const char* mod_revision, const char* submod_name, const char* submod_revision) -> std::optional<libyang::ModuleInfo> {
+    auto lambda = [clb](const auto mod_name, const auto mod_revision, const auto submod_name, const auto submod_revision) -> std::optional<libyang::ModuleInfo> {
         (void)submod_revision;
         auto moduleSource = clb(mod_name, mod_revision, submod_name, submod_revision);
         if (moduleSource.empty()) {
             return std::nullopt;
         }
         return libyang::ModuleInfo {
-            .data = moduleSource.c_str(),
+            .data = moduleSource,
             .format = libyang::SchemaFormat::YANG
 
         };
@@ -420,12 +420,12 @@
 
         return std::optional<libyang::CreationOptions>{};
     }();
-    return m_context.newPath2(path.c_str(), value ? value->c_str() : nullptr, options);
+    return m_context.newPath2(path, value, options);
 }
 
 std::optional<libyang::Module> YangSchema::getYangModule(const std::string& name)
 {
-    return m_context.getModuleImplemented(name.c_str());
+    return m_context.getModuleImplemented(name);
 }
 
 namespace {
diff --git a/src/yang_schema.hpp b/src/yang_schema.hpp
index 82d9acf..fe5cf1c 100644
--- a/src/yang_schema.hpp
+++ b/src/yang_schema.hpp
@@ -42,7 +42,7 @@
     [[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);
+    void registerModuleCallback(const std::function<std::string(const std::string_view, const std::optional<std::string_view>, const std::optional<std::string_view>, const std::optional<std::string_view>)>& clb);
 
     /** @short Loads a module called moduleName. */
     void loadModule(const std::string& moduleName);
diff --git a/submodules/dependencies b/submodules/dependencies
index f47525e..07f0bd8 160000
--- a/submodules/dependencies
+++ b/submodules/dependencies
@@ -1 +1 @@
-Subproject commit f47525e5878d57e748f066f8345d6437ae9c8bad
+Subproject commit 07f0bd86e8a12767e14ac5e3f51558e6d05e438a
diff --git a/tests/datastore_access.cpp b/tests/datastore_access.cpp
index 57871c0..1f8bb55 100644
--- a/tests/datastore_access.cpp
+++ b/tests/datastore_access.cpp
@@ -901,7 +901,7 @@
             // `xpath` holds the subscription xpath which won't have list keys. We need the path with list keys and
             // we'll find that in the input.
             auto inputPath = input.findXPath("/example-schema:ports/shutdown").front().path();
-            output.newPath(joinPaths(std::string{inputPath}, "success").c_str(), "true", libyang::CreationOptions::Output);
+            output.newPath(joinPaths(inputPath, "success"), "true", libyang::CreationOptions::Output);
             return sysrepo::ErrorCode::Ok;
         }
         throw std::runtime_error("unrecognized RPC");
@@ -939,21 +939,21 @@
 
                 if (inputNode.path() == nukes + "/payload/kilotons") {
                     kilotons = std::get<uint64_t>(inputNode.asTerm().value());
-                } else if (std::string_view{inputNode.path().get().get()}.find(nukes + "/cities") == 0) {
+                } else if (inputNode.path().find(nukes + "/cities") == 0) {
                     hasCities = true;
                 } else {
-                    throw std::runtime_error("RPC launch-nukes: unexpected input "s + inputNode.path().get().get());
+                    throw std::runtime_error("RPC launch-nukes: unexpected input "s + inputNode.path());
                 }
             }
             if (kilotons == 333'666) {
                 // magic, just do not generate any output. This is important because the NETCONF RPC returns just <ok/>.
                 return sysrepo::ErrorCode::Ok;
             }
-            output.newPath((nukes + "/blast-radius").c_str(), "33666", libyang::CreationOptions::Output);
-            output.newPath((nukes + "/actual-yield").c_str(), std::to_string(static_cast<uint64_t>(1.33 * kilotons)).c_str(), libyang::CreationOptions::Output);
+            output.newPath(nukes + "/blast-radius", "33666", libyang::CreationOptions::Output);
+            output.newPath(nukes + "/actual-yield", std::to_string(static_cast<uint64_t>(1.33 * kilotons)), libyang::CreationOptions::Output);
             if (hasCities) {
-                output.newPath((nukes + "/damaged-places/targets[city='London']/city").c_str(), "London", libyang::CreationOptions::Output);
-                output.newPath((nukes + "/damaged-places/targets[city='Berlin']/city").c_str(), "Berlin", libyang::CreationOptions::Output);
+                output.newPath(nukes + "/damaged-places/targets[city='London']/city", "London", libyang::CreationOptions::Output);
+                output.newPath(nukes + "/damaged-places/targets[city='Berlin']/city", "Berlin", libyang::CreationOptions::Output);
             }
             return sysrepo::ErrorCode::Ok;
         }
diff --git a/tests/mock/sysrepo_subscription.cpp b/tests/mock/sysrepo_subscription.cpp
index d4827bb..94d4c44 100644
--- a/tests/mock/sysrepo_subscription.cpp
+++ b/tests/mock/sysrepo_subscription.cpp
@@ -34,7 +34,7 @@
             return sysrepo::ErrorCode::Ok;
         }
 
-        for (const auto& it : sess.getChanges(("/"s + module_name.data() + ":*//.").c_str())) {
+        for (const auto& it : sess.getChanges("/"s + module_name.data() + ":*//.")) {
             auto xpath = it.node.path();
             std::optional<std::string> oldValue;
             std::optional<std::string> newValue;
@@ -72,7 +72,7 @@
 
 SysrepoSubscription::SysrepoSubscription(const std::string& moduleName, Recorder* rec, sysrepo::Datastore ds)
     : m_subscription([&moduleName, &rec, ds] { // This is an immediately invoked lambda.
-        return sysrepo::Connection{}.sessionStart(ds).onModuleChange(moduleName.c_str(),
+        return sysrepo::Connection{}.sessionStart(ds).onModuleChange(moduleName,
                 rec ? sysrepo::ModuleChangeCb{MyCallback{moduleName, rec}}
                 : sysrepo::ModuleChangeCb{[](auto, auto, auto, auto, auto, auto) { return sysrepo::ErrorCode::Ok; }});
     }())
@@ -97,9 +97,9 @@
         auto data = m_dataSupplier.get_data(subXPath->data());
         for (const auto& [p, v] : data) {
             if (!output) {
-                output = session.getContext().newPath(p.c_str(), v.type() == typeid(empty_) ? nullptr : leafDataToString(v).c_str());
+                output = session.getContext().newPath(p, v.type() == typeid(empty_) ? std::nullopt : std::optional<std::string>(leafDataToString(v)));
             } else {
-                output->newPath(p.c_str(), v.type() == typeid(empty_) ? nullptr : leafDataToString(v).c_str());
+                output->newPath(p, v.type() == typeid(empty_) ? std::nullopt : std::optional<std::string>(leafDataToString(v)));
             }
         }
         return sysrepo::ErrorCode::Ok;
@@ -110,6 +110,6 @@
 };
 
 OperationalDataSubscription::OperationalDataSubscription(const std::string& moduleName, const std::string& path, const DataSupplier& dataSupplier)
-    : m_subscription(sysrepo::Connection{}.sessionStart().onOperGet(moduleName.c_str(), OperationalDataCallback{dataSupplier}, path.c_str()))
+    : m_subscription(sysrepo::Connection{}.sessionStart().onOperGet(moduleName, OperationalDataCallback{dataSupplier}, path))
 {
 }
diff --git a/tests/utils.cpp b/tests/utils.cpp
index c24f4de..7d0af08 100644
--- a/tests/utils.cpp
+++ b/tests/utils.cpp
@@ -357,7 +357,7 @@
             expectedLeafData = std::string{"Xaver"};
         }
 
-        auto leaf = dataNode->findPath(("/" + path).c_str());
+        auto leaf = dataNode->findPath("/" + path);
         REQUIRE(leafValueFromNode(leaf->asTerm()) == expectedLeafData);
     }
 
diff --git a/tests/yang.cpp b/tests/yang.cpp
index 205616c..2560f64 100644
--- a/tests/yang.cpp
+++ b/tests/yang.cpp
@@ -477,21 +477,19 @@
 
 TEST_CASE("yangschema")
 {
-    using namespace std::string_literals;
-    using namespace std::string_view_literals;
     YangSchema ys;
-    ys.registerModuleCallback([]([[maybe_unused]] auto modName, auto, auto subModule, auto) {
-        if (modName != "example-schema"sv) {
-            throw std::logic_error("unrecognized module "s + modName);
+    ys.registerModuleCallback([](const auto modName, auto, const auto subModule, auto) {
+        if (modName != "example-schema") {
+            throw std::logic_error("unrecognized module " + std::string{modName});
         }
-        if (subModule == nullptr) {
+        if (!subModule) {
             return example_schema;
         }
-        if (subModule == "sub-module"sv) {
+        if (*subModule == "sub-module") {
             return included_submodule;
         }
 
-        throw std::logic_error("unrecognized submodule "s + subModule);
+        throw std::logic_error("unrecognized submodule " + std::string{*subModule});
     });
     ys.addSchemaString(second_schema);
     ys.addSchemaString(R"(