/* * This file is part of smolbote. It's copyrighted by the contributors recorded * in the version control history of the file, available from its original * location: https://neueland.iserlohn-fortress.net/gitea/aqua/smolbote * * SPDX-License-Identifier: GPL-3.0 */ #ifndef SMOLBOTE_CONFIGURATION_H #define SMOLBOTE_CONFIGURATION_H #include #include #include #include #include #include #include #include #include using conf_value_t = std::variant; template concept concept_value_t = std::is_arithmetic_v || std::is_same_v || std::is_constructible_v; class Configuration : private std::unordered_map { friend std::ostream &operator<<(std::ostream &out, const Configuration &obj); public: using kv_pair = std::pair; Configuration(); Configuration(std::initializer_list> p_list) noexcept; Configuration(const Configuration &) = delete; Configuration &operator=(const Configuration &) = delete; Configuration(Configuration &&other) = default; Configuration &operator=(Configuration &&) = delete; ~Configuration() = default; bool read_file(const std::string &location); void read(std::basic_istream &input); template [[nodiscard]] std::optional value(const char *path, const std::source_location location = std::source_location::current()) const { if(use_global) { return instance()->value(path, location); } if(count(path) == 0) { return std::nullopt; } return std::get(at(path)); } template [[nodiscard]] std::optional value(const char *p_path, const std::source_location location = std::source_location::current()) const { if(use_global) { return instance()->value(p_path, location); } if(this->count(p_path) == 0) { spdlog::warn("requested non-existent configuration value {} at {}:{}", p_path, location.file_name(), location.line()); return std::nullopt; } // path is guaranteed to exist const auto value = at(p_path); if(std::holds_alternative(value)) { if constexpr(std::is_arithmetic::value) { return static_cast(std::get(value)); } else if constexpr(std::is_constructible::value) { return T{ std::to_string(std::get(value)) }; } } else if(std::holds_alternative(value)) { if constexpr(std::is_constructible_v || std::is_same_v) { return std::get(value); } else if constexpr(std::is_arithmetic_v) { return static_cast(std::get(value)); } else if constexpr(std::is_constructible_v) { return std::get(value) ? T{ "true" } : T{ "false" }; } } else if(std::holds_alternative(value)) { auto str = std::get(value); try { if constexpr(std::is_same_v) { return (str == "true"); } else if constexpr(std::is_floating_point::value) { return static_cast(std::stod(str)); } else if constexpr(std::is_arithmetic::value) { return static_cast(std::stol(str)); } } catch(std::invalid_argument &) { return std::nullopt; } if(str.front() == '~') { str.replace(0, 1, m_homePath); } if constexpr(std::is_constructible::value) { return T{ str }; } } return std::nullopt; } template T &shortcut(T & /* unused */, const char * /* unused */, const std::source_location location = std::source_location::current()) const { return T{}; } static std::string init_global(const std::string &p_path); static void move_global(std::unique_ptr && conf); protected: void insert_or_assign(const kv_pair &pair); private: static Configuration *instance(); const std::string m_homePath; const bool use_global = false; }; #ifndef NO_QT_SPEC #include "qt_specialization.h" #endif std::ostream &operator<<(std::ostream &out, const Configuration &obj); #endif // SMOLBOTE_CONFIGURATION_H