diff options
| author | Lexi Winter <lexi@le-fay.org> | 2025-07-01 17:07:04 +0100 |
|---|---|---|
| committer | Lexi Winter <lexi@le-fay.org> | 2025-07-01 17:07:04 +0100 |
| commit | 2e2d1bd3b6c7776b77c33b94f30ead89367a71e6 (patch) | |
| tree | 54d37ffadf8e677938d9b7a28e4e9b71be1e75c1 /nihil.config | |
| parent | 36427c0966faa7aecd586b397ed9b845f18172f5 (diff) | |
| download | nihil-2e2d1bd3b6c7776b77c33b94f30ead89367a71e6.tar.gz nihil-2e2d1bd3b6c7776b77c33b94f30ead89367a71e6.tar.bz2 | |
add nihil.std
Diffstat (limited to 'nihil.config')
| -rw-r--r-- | nihil.config/CMakeLists.txt | 17 | ||||
| -rw-r--r-- | nihil.config/nihil.config.ccm | 7 | ||||
| -rw-r--r-- | nihil.config/option.cc | 20 | ||||
| -rw-r--r-- | nihil.config/option.ccm | 37 | ||||
| -rw-r--r-- | nihil.config/read.cc | 50 | ||||
| -rw-r--r-- | nihil.config/read.ccm | 47 | ||||
| -rw-r--r-- | nihil.config/store.cc | 14 | ||||
| -rw-r--r-- | nihil.config/store.ccm | 27 | ||||
| -rw-r--r-- | nihil.config/string.cc | 62 | ||||
| -rw-r--r-- | nihil.config/string.ccm | 95 | ||||
| -rw-r--r-- | nihil.config/string.test.cc | 32 | ||||
| -rw-r--r-- | nihil.config/tests/CMakeLists.txt | 13 | ||||
| -rw-r--r-- | nihil.config/tests/string.cc | 36 | ||||
| -rw-r--r-- | nihil.config/write.cc | 41 | ||||
| -rw-r--r-- | nihil.config/write.ccm | 42 |
15 files changed, 191 insertions, 349 deletions
diff --git a/nihil.config/CMakeLists.txt b/nihil.config/CMakeLists.txt index 8a52d3c..6ed3651 100644 --- a/nihil.config/CMakeLists.txt +++ b/nihil.config/CMakeLists.txt @@ -2,6 +2,7 @@ add_library(nihil.config STATIC) target_link_libraries(nihil.config PRIVATE + nihil.std nihil.error nihil.generator nihil.posix @@ -19,13 +20,21 @@ target_sources(nihil.config PRIVATE option.cc - read.cc store.cc - string.cc - write.cc ) if(NIHIL_TESTS) - add_subdirectory(tests) + add_executable(nihil.config.test + string.test.cc + ) + + target_link_libraries(nihil.config.test PRIVATE + nihil.config + Catch2::Catch2WithMain) + + include(CTest) + include(Catch) + catch_discover_tests(nihil.config.test) + enable_testing() endif() diff --git a/nihil.config/nihil.config.ccm b/nihil.config/nihil.config.ccm index 8957305..8eedf22 100644 --- a/nihil.config/nihil.config.ccm +++ b/nihil.config/nihil.config.ccm @@ -1,9 +1,4 @@ -/* - * This source code is released into the public domain. - */ - -module; - +// This source code is released into the public domain. export module nihil.config; export import :option; diff --git a/nihil.config/option.cc b/nihil.config/option.cc index 886f4b6..e09842e 100644 --- a/nihil.config/option.cc +++ b/nihil.config/option.cc @@ -1,16 +1,7 @@ -/* - * This source code is released into the public domain. - */ - -module; - -#include <coroutine> -#include <expected> -#include <iostream> -#include <string> - +// This source code is released into the public domain. module nihil.config; +import nihil.std; import nihil.error; import nihil.monad; import nihil.ucl; @@ -18,7 +9,7 @@ import nihil.ucl; namespace nihil::config { //NOLINTNEXTLINE(bugprone-easily-swappable-parameters) -option::option(std::string_view name, std::string_view description) +option::option(std::string_view const name, std::string_view const description) : m_name(name) , m_description(description) { @@ -26,16 +17,15 @@ option::option(std::string_view name, std::string_view description) if (okay) return; - std::print(std::cerr, + std::println(std::cerr, "INTERNAL ERROR: failed to register " "configuration option '{}': {}", m_name, okay.error()); - std::exit(1); + std::exit(1); // NOLINT } option::~option() { - std::ignore = store::get().unregister_option(this); } auto option::name(this option const &self) noexcept diff --git a/nihil.config/option.ccm b/nihil.config/option.ccm index 4b95793..0758c1a 100644 --- a/nihil.config/option.ccm +++ b/nihil.config/option.ccm @@ -1,32 +1,22 @@ -/* - * This source code is released into the public domain. - */ - -module; - -#include <expected> -#include <iosfwd> -#include <string> - +// This source code is released into the public domain. export module nihil.config:option; +import nihil.std; import nihil.error; import nihil.ucl; namespace nihil::config { -/* - * Base class for options; this is what config_store interacts with. - * - * Base classes should override the four protected functions: - * - * get_string() - * set_string() - * get_ucl() - * set_ucl() - * - * Overriding any other members is not permitted. - */ +// Base class for options; this is what config_store interacts with. +// +// Base classes should override the four protected functions: +// +// get_string() +// set_string() +// get_ucl() +// set_ucl() +// +// Overriding any other members is not permitted. export struct option { @@ -70,6 +60,9 @@ export struct option option(option const &) = delete; auto operator=(option const &) -> option& = delete; + option(option &&) = delete; + auto operator=(option &&) -> option& = delete; + protected: option(std::string_view name, std::string_view description); diff --git a/nihil.config/read.cc b/nihil.config/read.cc deleted file mode 100644 index 48484fb..0000000 --- a/nihil.config/read.cc +++ /dev/null @@ -1,50 +0,0 @@ -/* - * This source code is released into the public domain. - */ - -module; - -#include <coroutine> -#include <expected> -#include <filesystem> -#include <format> -#include <iterator> -#include <string> - -module nihil.config; - -import nihil.error; -import nihil.monad; -import nihil.posix; -import nihil.ucl; - -namespace nihil::config { - -auto read_from(std::filesystem::path const &filename) - -> std::expected<void, error> -{ - // TODO: nihil.ucl should have a way to load UCL from a filename. - - std::string config_text; - auto err = read_file(filename, std::back_inserter(config_text)); - if (!err) { - // Ignore ENOENT, it simply means we haven't created the - // config file yet, so default values will be used. - if (err.error().root_cause() == std::errc::no_such_file_or_directory) - co_return {}; - auto errstr = std::format("cannot read {}", filename.string()); - co_return std::unexpected(error(errstr, err.error())); - } - - // Parse the UCL. - auto uclconfig = co_await ucl::parse(config_text); - - for (auto &&[key, value] : uclconfig) { - auto opt = co_await store::get().fetch(key); - co_await opt->ucl(value); - } - - co_return {}; -} - -} // namespace nihil::config diff --git a/nihil.config/read.ccm b/nihil.config/read.ccm index 9cf28c9..7065492 100644 --- a/nihil.config/read.ccm +++ b/nihil.config/read.ccm @@ -1,22 +1,41 @@ -/* - * This source code is released into the public domain. - */ - -module; - -#include <expected> -#include <filesystem> - +// This source code is released into the public domain. export module nihil.config:read; +import nihil.std; import nihil.error; +import nihil.monad; +import nihil.posix; +import nihil.ucl; +import :option; +import :store; namespace nihil::config { -/* - * Load the configuration from a file. - */ -export [[nodiscard]] auto read_from(std::filesystem::path const &filename) - -> std::expected<void, error>; +// Load the configuration from a file. +export [[nodiscard]] auto +read_from(std::filesystem::path const &filename) -> std::expected<void, error> +{ + // TODO: nihil.ucl should have a way to load UCL from a filename. + + auto config_text = std::string(); + auto err = read_file(filename, std::back_inserter(config_text)); + if (!err) { + // Ignore ENOENT, it simply means we haven't created the + // config file yet, so default values will be used. + if (err.error().root_cause() == std::errc::no_such_file_or_directory) + co_return {}; + co_return std::unexpected(error(std::format("cannot read {}", filename.string()), err.error())); + } + + // Parse the UCL. + auto uclconfig = co_await ucl::parse(config_text); + + for (auto &&[key, value] : uclconfig) { + auto *opt = co_await store::get().fetch(key); + co_await opt->ucl(value); + } + + co_return {}; +} } // namespace nihil::config diff --git a/nihil.config/store.cc b/nihil.config/store.cc index 0fb8cc0..e1ca271 100644 --- a/nihil.config/store.cc +++ b/nihil.config/store.cc @@ -1,17 +1,7 @@ -/* - * This source code is released into the public domain. - */ - -module; - -#include <coroutine> -#include <expected> -#include <filesystem> -#include <format> -#include <map> - +// This source code is released into the public domain. module nihil.config; +import nihil.std; import nihil.error; import nihil.generator; import nihil.monad; diff --git a/nihil.config/store.ccm b/nihil.config/store.ccm index 4d37ce0..0a92ef0 100644 --- a/nihil.config/store.ccm +++ b/nihil.config/store.ccm @@ -1,25 +1,16 @@ -/* - * This source code is released into the public domain. - */ - -module; - -/* - * The configuration store. There should only be one of these. - */ - -#include <coroutine> -#include <expected> -#include <string> -#include <map> - +// This source code is released into the public domain. export module nihil.config:store; +// The configuration store. There should only be one of these. + +import nihil.std; +import nihil.error; import nihil.generator; -import :option; namespace nihil::config { +export struct option; + struct store final { /* * Get the global config store. @@ -57,8 +48,8 @@ struct store final { // Not movable or copyable. store(store const &) = delete; store(store &&) = delete; - store& operator=(store const &) = delete; - store& operator=(store &&) = delete; + auto operator=(store const &) -> store & = delete; + auto operator=(store &&) -> store & = delete; private: store(); diff --git a/nihil.config/string.cc b/nihil.config/string.cc deleted file mode 100644 index 0ca4605..0000000 --- a/nihil.config/string.cc +++ /dev/null @@ -1,62 +0,0 @@ -/* - * This source code is released into the public domain. - */ - -module; - -#include <coroutine> -#include <expected> -#include <format> -#include <string> - -module nihil.config; - -import nihil.error; -import nihil.monad; -import nihil.ucl; - -namespace nihil::config { - -string::string( - std::string &storage, - std::string_view name, - std::string_view description) noexcept - : option(name, description) - , m_storage(storage) -{ -} - -string::~string() = default; - -auto string::get_string() const -> std::string -{ - return m_storage; -} - -auto string::set_string(std::string_view new_value) - -> std::expected<void, error> -{ - m_storage = new_value; - return {}; -} - -auto string::get_ucl() const -> std::expected<ucl::object, error> -{ - return ucl::string(m_storage); -} - -auto string::set_ucl(ucl::object const &uclobj) -> std::expected<void, error> -{ - auto obj = co_await object_cast<ucl::string>(uclobj) - .transform_error([&] (ucl::type_mismatch const &m) { - return error(std::format( - "'{}': expected string, not {}", - name(), str(m.actual_type()))); - }); - - m_storage = obj.value(); - is_default(false); - co_return {}; -} - -} // namespace nihil::config diff --git a/nihil.config/string.ccm b/nihil.config/string.ccm index 668bbc0..12ede7a 100644 --- a/nihil.config/string.ccm +++ b/nihil.config/string.ccm @@ -1,55 +1,68 @@ -/* - * This source code is released into the public domain. - */ - -module; - -#include <expected> -#include <format> -#include <string> - +// This source code is released into the public domain. export module nihil.config:string; +import nihil.std; +import nihil.monad; import nihil.ucl; import :option; namespace nihil::config { -/* - * A string option. The backing type is std::string. - */ +// A string option. The backing type is std::string. export struct string final : option { - string(std::string &storage, - std::string_view name, - std::string_view description) noexcept; - - ~string(); - - /* - * Get this option as a string; simply returns the storage. - */ - [[nodiscard]] auto get_string() const -> std::string override; - - /* - * Set this option to a string value; assigns to the storage. - */ - [[nodiscard]] auto set_string(std::string_view new_value) - -> std::expected<void, error> override; - - /* - * Convert this option to a UCL object. - */ - [[nodiscard]] auto get_ucl() const - -> std::expected<ucl::object, error> override; - - /* - * Set this option from a UCL object. - */ - [[nodiscard]] auto set_ucl(ucl::object const &uclobj) - -> std::expected<void, error> override; + string(std::string &storage, std::string_view const name, + std::string_view const description) noexcept + : option(name, description) + , m_storage(storage) + { + } + + ~string() override = default; + + // Not copyable. + string(string const &) = delete; + auto operator=(string const &) -> string & = delete; + + // Not movable. + string(string &&) = delete; + auto operator=(string &&) -> string & = delete; private: + // Get this option as a string; simply returns the storage. + [[nodiscard]] auto get_string() const -> std::string override + { + return m_storage; + } + + // Set this option to a string value; assigns to the storage. + [[nodiscard]] auto + set_string(std::string_view const new_value) -> std::expected<void, error> override + { + m_storage = new_value; + return {}; + } + + // Convert this option to a UCL object. + [[nodiscard]] auto get_ucl() const -> std::expected<ucl::object, error> override + { + return ucl::make_string(m_storage); + } + + // Set this option from a UCL object. + [[nodiscard]] auto set_ucl(ucl::object const &uclobj) -> std::expected<void, error> override + { + auto obj = co_await object_cast<ucl::string>(uclobj).transform_error( + [&](ucl::type_mismatch const &m) { + return error(std::format("'{}': expected string, not {}", name(), + str(m.actual_type()))); + }); + + m_storage = obj.value(); + is_default(false); + co_return {}; + } + std::string &m_storage; }; diff --git a/nihil.config/string.test.cc b/nihil.config/string.test.cc new file mode 100644 index 0000000..322eb79 --- /dev/null +++ b/nihil.config/string.test.cc @@ -0,0 +1,32 @@ +// This source code is released into the public domain. + +#include <catch2/catch_test_macros.hpp> + +import nihil.std; +import nihil.config; + +namespace { +TEST_CASE("nihil.config: string option", "[nihil][nihil.config]") +{ + auto storage = std::string(); + + REQUIRE(nihil::config::get_option("test_option").has_value() == false); + + { + auto string_option = nihil::config::string( + storage, "test_option", "This is a test option"); + + auto *opt = nihil::config::get_option("test_option").value(); + + REQUIRE(opt->name() == "test_option"); + REQUIRE(opt->description() == "This is a test option"); + REQUIRE(opt->is_default() == true); + REQUIRE(opt->string() == ""); + + REQUIRE(opt->string("testing")); + REQUIRE(storage == "testing"); + } + + REQUIRE(nihil::config::get_option("test_option").has_value() == false); +} +} // anonymous namespace diff --git a/nihil.config/tests/CMakeLists.txt b/nihil.config/tests/CMakeLists.txt deleted file mode 100644 index ffa60c3..0000000 --- a/nihil.config/tests/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -# This source code is released into the public domain. - -add_executable(nihil.config.test - string.cc -) - -target_link_libraries(nihil.config.test PRIVATE - nihil.config - Catch2::Catch2WithMain) - -include(CTest) -include(Catch) -catch_discover_tests(nihil.config.test) diff --git a/nihil.config/tests/string.cc b/nihil.config/tests/string.cc deleted file mode 100644 index aeb1ef8..0000000 --- a/nihil.config/tests/string.cc +++ /dev/null @@ -1,36 +0,0 @@ -/* - * This source code is released into the public domain. - */ - -#include <string> - -#include <catch2/catch_test_macros.hpp> - -import nihil.config; - -TEST_CASE("nihil.config: string option", "[nihil][nihil.config]") -{ - std::string storage; - - auto opt = nihil::config::get_option("test_option"); - REQUIRE(!opt); - - { - auto string_option = nihil::config::string( - storage, "test_option", "This is a test option"); - - auto opt = nihil::config::get_option("test_option"); - REQUIRE(opt); - - REQUIRE((*opt)->name() == "test_option"); - REQUIRE((*opt)->description() == "This is a test option"); - REQUIRE((*opt)->is_default() == true); - REQUIRE((*opt)->string() == ""); - - REQUIRE((*opt)->string("testing")); - REQUIRE(storage == "testing"); - } - - opt = nihil::config::get_option("test_option"); - REQUIRE(!opt); -} diff --git a/nihil.config/write.cc b/nihil.config/write.cc deleted file mode 100644 index 80125a8..0000000 --- a/nihil.config/write.cc +++ /dev/null @@ -1,41 +0,0 @@ -/* - * This source code is released into the public domain. - */ - -module; - -#include <coroutine> -#include <expected> -#include <filesystem> -#include <format> -#include <utility> - -module nihil.config; - -import nihil.error; -import nihil.monad; -import nihil.posix; -import nihil.ucl; - -namespace nihil::config { - -auto write_to(std::filesystem::path const &filename) - -> std::expected<void, error> -{ - auto uclconfig = ucl::map<ucl::object>(); - - // Add all the options to the UCL object. - for (auto const &option : store::get().all()) { - if (option->is_default()) - continue; - - auto uobj = co_await option->ucl(); - uclconfig.insert({option->name(), uobj}); - } - - auto ucl_text = std::format("{:c}", uclconfig); - co_await safe_write_file(filename, ucl_text); - co_return {}; -} - -}; diff --git a/nihil.config/write.ccm b/nihil.config/write.ccm index 564bb20..a7eddd5 100644 --- a/nihil.config/write.ccm +++ b/nihil.config/write.ccm @@ -1,22 +1,34 @@ -/* - * This source code is released into the public domain. - */ - -module; - -#include <expected> -#include <filesystem> - +// This source code is released into the public domain. export module nihil.config:write; +import nihil.std; import nihil.error; +import nihil.monad; +import nihil.posix; +import nihil.ucl; +import :option; +import :store; namespace nihil::config { -/* - * Write all config values (except defaults) to disk. - */ -export [[nodiscard]] auto write_to(std::filesystem::path const &filename) -> - std::expected<void, error>; +// Write all config values (except defaults) to disk. +export [[nodiscard]] auto +write_to(std::filesystem::path const &filename) -> std::expected<void, error> +{ + auto uclconfig = ucl::map<ucl::object>(); + + // Add all the options to the UCL object. + for (auto const &option : store::get().all()) { + if (option->is_default()) + continue; + + auto uobj = co_await option->ucl(); + uclconfig.insert({option->name(), uobj}); + } + + auto ucl_text = std::format("{:c}", uclconfig); + co_await safe_write_file(filename, ucl_text); + co_return {}; +} -}; +}; // namespace nihil::config |
