/* * 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 #if defined(__clang__) #define consumable(X) [[clang::consumable(X)]] #define return_typestate(X) [[clang::return_typestate(X)]] #define callable_when(X) [[clang::callable_when(X)]] #define param_typestate(X) [[clang::param_typestate(X)]] #else #define consumable(X) #define return_typestate(X) #define callable_when(X) #define param_typestate(X) #endif typedef std::variant conf_value_t; template concept concept_value_t = std::is_arithmetic::value || std::is_same::value || std::is_constructible::value; class consumable(unconsumed) Configuration : private std::unordered_map { friend std::ostream &operator<<(std::ostream &out, const Configuration &obj); public: return_typestate(unconsumed) Configuration(); return_typestate(unconsumed) Configuration(std::initializer_list> l) noexcept; return_typestate(unconsumed) Configuration(const Configuration &other param_typestate(unconsumed)) = default; Configuration &operator=(const Configuration &) = delete; return_typestate(unconsumed) Configuration(Configuration && other param_typestate(unconsumed)) = default; Configuration &operator=(Configuration &&) = delete; ~Configuration() = default; callable_when(unconsumed) void read_file(const std::string &location); callable_when(unconsumed) void read(std::basic_istream & input); template callable_when(unconsumed) [[nodiscard]] std::optional value(const char *path) const { if(use_global) { return instance()->value(path); } if(count(path) == 0) { return std::nullopt; } return std::get(at(path)); } template callable_when(unconsumed) [[nodiscard]] std::optional value(const char *path) const { if(use_global) { return instance()->value(path); } if(this->count(path) == 0) { return std::nullopt; } // path is guaranteed to exist const auto value = at(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::value) { return std::get(value); } else if constexpr(std::is_constructible::value) { return std::get(value) ? T{ "true" } : T{ "false" }; } } else if(std::holds_alternative(value)) { auto str = std::get(value); try { 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 callable_when(unconsumed) T &shortcut(T & /* unused */, const char * /* unused */) const { return T{}; } static void move_global(std::unique_ptr && conf); private: static Configuration *instance(); const std::string m_homePath; const bool use_global = false; }; #include "qt_specialization.h" std::ostream &operator<<(std::ostream &out, const Configuration &obj); #endif // SMOLBOTE_CONFIGURATION_H