34#include <unordered_map>
35#include <unordered_set>
43using config_map = std::unordered_map<std::string, std::string>;
63 static T
get(
const config_map& config,
const std::string& key,
const T& default_value) {
64 auto it = config.find(key);
65 if (it == config.end()) {
80 auto it = config.find(key);
81 if (it == config.end()) {
94 return config.find(key) != config.end();
107 template <
typename T>
109 const T& min_value,
const T& max_value) {
110 static_assert(std::is_arithmetic_v<T>,
"get_clamped requires arithmetic type");
111 T value =
get<T>(config, key, default_value);
112 if (value < min_value) {
115 if (value > max_value) {
130 template <
typename T>
132 const std::unordered_set<T>& allowed_values) {
133 T value =
get<T>(config, key, default_value);
134 if (allowed_values.find(value) != allowed_values.end()) {
137 return default_value;
149 const std::string& default_value,
const std::string& pattern) {
150 auto it = config.find(key);
151 if (it == config.end()) {
152 return default_value;
155 std::regex regex(pattern);
156 if (std::regex_match(it->second, regex)) {
162 return default_value;
174 template <
typename T>
176 std::function<
bool(
const T&)> validator) {
177 T value =
get<T>(config, key, default_value);
178 if (validator(value)) {
181 return default_value;
196 template <
typename Duration>
198 const Duration& default_value) {
199 auto it = config.find(key);
200 if (it == config.end()) {
201 return default_value;
214 template <
typename T>
216 const std::vector<T>& default_values) {
217 auto it = config.find(key);
218 if (it == config.end()) {
219 return default_values;
228 template <
typename T>
229 static T
parse_value(
const std::string& str,
const T& default_value) {
231 if constexpr (std::is_same_v<T, bool>) {
233 }
else if constexpr (std::is_same_v<T, std::string>) {
235 }
else if constexpr (std::is_integral_v<T>) {
237 }
else if constexpr (std::is_floating_point_v<T>) {
240 return default_value;
243 return default_value;
250 template <
typename T>
253 if constexpr (std::is_same_v<T, bool>) {
255 }
else if constexpr (std::is_same_v<T, std::string>) {
257 }
else if constexpr (std::is_integral_v<T>) {
259 }
else if constexpr (std::is_floating_point_v<T>) {
279 std::string lower = str;
280 for (
auto& c : lower) {
281 c =
static_cast<char>(std::tolower(
static_cast<unsigned char>(c)));
283 return lower ==
"true" || lower ==
"1" || lower ==
"yes" || lower ==
"on";
289 template <
typename T>
291 static_assert(std::is_integral_v<T>,
"parse_integral requires integral type");
292 if constexpr (std::is_signed_v<T>) {
293 long long value = std::stoll(str);
294 return static_cast<T
>(value);
296 unsigned long long value = std::stoull(str);
297 return static_cast<T
>(value);
304 template <
typename T>
306 static_assert(std::is_floating_point_v<T>,
"parse_floating requires floating point type");
307 if constexpr (std::is_same_v<T, float>) {
308 return std::stof(str);
309 }
else if constexpr (std::is_same_v<T, double>) {
310 return std::stod(str);
312 return static_cast<T
>(std::stold(str));
320 template <
typename Duration>
321 static Duration
parse_duration(
const std::string& str,
const Duration& default_value) {
324 return default_value;
328 size_t suffix_start = str.find_first_not_of(
"0123456789.-");
330 if (suffix_start == std::string::npos) {
332 long long value = std::stoll(str);
333 return Duration(value);
337 long long value = std::stoll(str.substr(0, suffix_start));
338 std::string suffix = str.substr(suffix_start);
341 while (!suffix.empty() && std::isspace(
static_cast<unsigned char>(suffix.front()))) {
346 for (
auto& c : suffix) {
347 c =
static_cast<char>(std::tolower(
static_cast<unsigned char>(c)));
351 if (suffix ==
"ms" || suffix ==
"millisecond" || suffix ==
"milliseconds") {
352 return std::chrono::duration_cast<Duration>(std::chrono::milliseconds(value));
353 }
else if (suffix ==
"s" || suffix ==
"sec" || suffix ==
"second" || suffix ==
"seconds") {
354 return std::chrono::duration_cast<Duration>(std::chrono::seconds(value));
355 }
else if (suffix ==
"m" || suffix ==
"min" || suffix ==
"minute" || suffix ==
"minutes") {
356 return std::chrono::duration_cast<Duration>(std::chrono::minutes(value));
357 }
else if (suffix ==
"h" || suffix ==
"hr" || suffix ==
"hour" || suffix ==
"hours") {
358 return std::chrono::duration_cast<Duration>(std::chrono::hours(value));
361 return Duration(value);
364 return default_value;
371 template <
typename T>
372 static std::vector<T>
parse_list(
const std::string& str,
const std::vector<T>& default_values) {
375 return default_values;
378 std::vector<T> result;
384 size_t start = current.find_first_not_of(
" \t");
385 size_t end = current.find_last_not_of(
" \t");
386 if (start != std::string::npos) {
387 std::string trimmed = current.substr(start, end - start + 1);
390 result.push_back(*parsed);
400 if (!current.empty()) {
401 size_t start = current.find_first_not_of(
" \t");
402 size_t end = current.find_last_not_of(
" \t");
403 if (start != std::string::npos) {
404 std::string trimmed = current.substr(start, end - start + 1);
407 result.push_back(*parsed);
412 return result.empty() ? default_values : result;
414 return default_values;
Unified configuration parsing utility.
static T parse_integral(const std::string &str)
Parse integral value from string.
static T get_validated(const config_map &config, const std::string &key, const T &default_value, std::function< bool(const T &)> validator)
Get a configuration value with custom validation.
static std::optional< T > parse_value_optional(const std::string &str)
Parse a string value to target type as optional.
static Duration get_duration(const config_map &config, const std::string &key, const Duration &default_value)
Get a duration value from configuration.
static bool parse_bool(const std::string &str)
Parse boolean value from string.
static bool has_key(const config_map &config, const std::string &key)
Check if a configuration key exists.
static Duration parse_duration(const std::string &str, const Duration &default_value)
Parse duration string with optional suffix Supported suffixes: ms (milliseconds), s (seconds),...
static T parse_value(const std::string &str, const T &default_value)
Parse a string value to target type with default fallback.
static std::optional< T > get_optional(const config_map &config, const std::string &key)
Get a configuration value as optional.
static std::vector< T > get_list(const config_map &config, const std::string &key, const std::vector< T > &default_values)
Get a list of values from a comma-separated string.
static T get_enum(const config_map &config, const std::string &key, const T &default_value, const std::unordered_set< T > &allowed_values)
Get a configuration value from a set of allowed values.
static T parse_floating(const std::string &str)
Parse floating-point value from string.
static std::vector< T > parse_list(const std::string &str, const std::vector< T > &default_values)
Parse comma-separated list of values.
static T get(const config_map &config, const std::string &key, const T &default_value)
Get a configuration value with type conversion.
static std::string get_matching(const config_map &config, const std::string &key, const std::string &default_value, const std::string &pattern)
Get a string configuration value matching a regex pattern.
static T get_clamped(const config_map &config, const std::string &key, const T &default_value, const T &min_value, const T &max_value)
Get a configuration value with validation.
std::unordered_map< std::string, std::string > config_map
Type alias for configuration map.