17#ifndef TOOLBOX_UTIL_CONFIG_HPP
18#define TOOLBOX_UTIL_CONFIG_HPP
26#include <unordered_map>
30inline namespace util {
32template <
typename FnT>
35 using namespace std::literals::string_literals;
38 std::unordered_map<std::string, KeyClassification, string_hash, std::equal_to<>>
key_class_map;
41 while (std::getline(
is,
line)) {
47 if (
line.empty() ||
line[0] ==
'#') {
51 if (
line.front() ==
'[' &&
line.back() ==
']') {
53 name->assign(
line, 1,
line.size() - 2);
71 std::optional<KeyClassification>
key_class;
78 throw std::runtime_error{
79 std::string{
key}.append(
" is already set as a single-valued key (with '=') "
80 "and cannot be reassigned with '+='")};
83 throw std::runtime_error{
84 std::string{
key}.append(
" is already set as a multi-valued key (with '+=') "
85 "and cannot be reassigned with '='")};
90 : KeyClassification::SingleValued;
93 fn(std::move(
key), std::move(
val));
116 const
std::
string& get(const
std::
string& key) const;
117 const
char* get(const
std::
string& key, const
char* dfl) const;
118 const
char* get(const
std::
string& key,
std::nullptr_t)
const
120 return get(key,
static_cast<const char*
>(
nullptr));
123 template <
typename ValueT>
124 ValueT
get(
const std::string& key)
const
126 const auto it{get_last_value(key)};
127 if (it != map_.end()) {
128 return transform_value<ValueT>(it->second);
131 throw std::runtime_error{std::string{
"missing config key: "} + key};
133 return parent_->get<ValueT>(key);
135 template <
typename ValueT>
136 ValueT
get(
const std::string& key, ValueT dfl)
const
138 const auto it{get_last_value(key)};
139 if (it != map_.end()) {
140 return transform_value<ValueT>(it->second);
142 return parent_ ? parent_->get<ValueT>(key, dfl) : dfl;
147 auto [begin, end] = map_.equal_range(key);
149 auto fn = [](
const auto& kvp) ->
const std::string& {
return kvp.second; };
150 auto rng = std::ranges::subrange(begin, end, map_.count(key)) | std::views::transform(fn);
156 return parent_ ? parent_->get_multi(key) : rng;
159 template <
typename ValueT>
161 return get_multi(key) | std::views::transform(transform_value<ValueT>);
164 std::size_t
size() const noexcept {
return map_.size(); }
165 void clear() noexcept { map_.clear(); }
170 return read_section(is, &next);
174 return read_section(is, next);
176 void insert(std::string key, std::string val)
178 map_.emplace(std::move(key), std::move(val));
182 template<
typename... ValueTs>
184 void set(const
std::
string& key, ValueTs ... vals)
186 static_assert(
sizeof...(ValueTs) > 0,
"at least 1 value is required");
189 (insert(key, std::string(std::move(vals))), ...);
195 template <
typename ValueT>
196 static ValueT transform_value(
const std::string& v)
198 if constexpr (std::is_same_v<ValueT, std::string_view>) {
201 return std::string_view{v};
202 }
else if constexpr (std::is_enum_v<ValueT>) {
203 return ValueT{from_string<std::underlying_type_t<ValueT>>(v)};
205 return from_string<ValueT>(v);
209 auto get_last_value(
const std::string& key)
const
210 -> std::multimap<std::string, std::string>::const_iterator
212 auto [first, second] = map_.equal_range(key);
213 return (first == second) ? map_.end() : std::prev(second);
216 std::istream& read_section(std::istream& is, std::string* next);
217 std::multimap<std::string, std::string> map_;
218 Config* parent_{
nullptr};
234 void clear() noexcept;
235 void read(
std::istream& is);
241 auto it = map_.find(name);
242 return it != map_.end() ? it->second : root_;
246 return section(std::string{name});
252 std::map<std::string, Config> map_;