40#include <unordered_map>
43#ifdef BUILD_WITH_YAML_CPP
44#include <yaml-cpp/yaml.h>
52namespace config_error_codes {
107#ifdef BUILD_WITH_YAML_CPP
109 if (!std::filesystem::exists(path)) {
112 "Configuration file not found: " + path,
118 std::ifstream file(path);
119 if (!file.is_open()) {
122 "Failed to open configuration file: " + path,
127 std::stringstream buffer;
128 buffer << file.rdbuf();
130 }
catch (
const std::exception& e) {
133 std::string(
"Failed to read configuration file: ") + e.what(),
142 "YAML support not available. Build with -DBUILD_WITH_YAML_CPP=ON",
158#ifdef BUILD_WITH_YAML_CPP
163 YAML::Node root = YAML::Load(expanded);
169 auto parse_result = parse_yaml(root, config);
170 if (parse_result.is_err()) {
176 if (env_result.is_err()) {
181 auto validation_result =
validate(config);
182 if (validation_result.is_err()) {
187 }
catch (
const YAML::ParserException& e) {
190 std::string(
"YAML parse error: ") + e.what(),
193 }
catch (
const std::exception& e) {
196 std::string(
"Failed to parse configuration: ") + e.what(),
204 "YAML support not available. Build with -DBUILD_WITH_YAML_CPP=ON",
222 if (result.is_err()) {
226 auto validation_result =
validate(config);
227 if (validation_result.is_err()) {
252 std::vector<validation_issue> issues;
270 for (
const auto& issue : issues) {
271 if (!issue.is_warning) {
274 "Validation failed for " + issue.field_path +
": " + issue.message,
292 std::vector<validation_issue> issues;
314 static const std::regex env_pattern(R
"(\$\{([^}]+)\})");
316 std::string result = value;
318 std::string::const_iterator search_start = result.cbegin();
323 while (std::regex_search(search_start, result.cend(), match, env_pattern)) {
324 size_t match_pos =
static_cast<size_t>(match.position()) +
325 static_cast<size_t>(std::distance(result.cbegin(), search_start));
328 output += result.substr(last_pos, match_pos - last_pos);
331 std::string var_name = match[1].str();
332 const char* env_value = std::getenv(var_name.c_str());
334 if (env_value !=
nullptr) {
338 output += match[0].str();
341 last_pos = match_pos + match[0].length();
342 search_start = match.suffix().first;
346 output += result.substr(last_pos);
417 const char* value = std::getenv(env_name);
418 if (value !=
nullptr) {
424 const char* value = std::getenv(env_name);
425 if (value !=
nullptr) {
427 target = std::stoull(value);
435 const char* value = std::getenv(env_name);
436 if (value !=
nullptr) {
438 auto parsed = std::stoul(value);
439 if (parsed <= 65535) {
440 target =
static_cast<uint16_t
>(parsed);
449 const char* value = std::getenv(env_name);
450 if (value !=
nullptr) {
451 std::string str_value(value);
453 std::transform(str_value.begin(), str_value.end(), str_value.begin(),
454 [](
unsigned char c) { return std::tolower(c); });
456 if (str_value ==
"true" || str_value ==
"1" || str_value ==
"yes" || str_value ==
"on") {
458 }
else if (str_value ==
"false" || str_value ==
"0" || str_value ==
"no" || str_value ==
"off") {
465 const char* value = std::getenv(env_name);
466 if (value !=
nullptr) {
468 target = std::stod(value);
476 const char* value = std::getenv(env_name);
477 if (value !=
nullptr) {
479 target = std::chrono::milliseconds{std::stoll(value)};
487 const char* value = std::getenv(env_name);
488 if (value !=
nullptr) {
490 std::string str_value(value);
491 std::stringstream ss(str_value);
493 while (std::getline(ss, item,
',')) {
495 size_t start = item.find_first_not_of(
" \t");
496 size_t end = item.find_last_not_of(
" \t");
497 if (start != std::string::npos && end != std::string::npos) {
498 target.push_back(item.substr(start, end - start + 1));
504#ifdef BUILD_WITH_YAML_CPP
511 YAML::Node system_node = root[
"unified_system"];
517 if (system_node[
"thread"]) {
518 parse_thread_config(system_node[
"thread"], config.
thread);
522 if (system_node[
"logger"]) {
523 parse_logger_config(system_node[
"logger"], config.
logger);
527 if (system_node[
"monitoring"]) {
528 parse_monitoring_config(system_node[
"monitoring"], config.
monitoring);
532 if (system_node[
"database"]) {
533 parse_database_config(system_node[
"database"], config.
database);
537 if (system_node[
"network"]) {
538 parse_network_config(system_node[
"network"], config.
network);
542 }
catch (
const YAML::Exception& e) {
545 std::string(
"YAML parse error: ") + e.what(),
551 static void parse_thread_config(
const YAML::Node& node, thread_config& config) {
552 if (node[
"pool_size"]) {
553 config.pool_size = node[
"pool_size"].as<
size_t>();
555 if (node[
"queue_type"]) {
556 config.queue_type = node[
"queue_type"].as<std::string>();
558 if (node[
"max_queue_size"]) {
559 config.max_queue_size = node[
"max_queue_size"].as<
size_t>();
561 if (node[
"thread_name_prefix"]) {
562 config.thread_name_prefix = node[
"thread_name_prefix"].as<std::string>();
566 static void parse_logger_config(
const YAML::Node& node, logger_config& config) {
568 config.level = node[
"level"].as<std::string>();
570 if (node[
"writers"]) {
571 config.writers.clear();
572 for (
const auto& writer : node[
"writers"]) {
573 config.writers.push_back(writer.as<std::string>());
577 config.async = node[
"async"].as<
bool>();
579 if (node[
"buffer_size"]) {
580 config.buffer_size = node[
"buffer_size"].as<
size_t>();
582 if (node[
"file_path"]) {
583 config.file_path = node[
"file_path"].as<std::string>();
585 if (node[
"max_file_size"]) {
586 config.max_file_size = node[
"max_file_size"].as<
size_t>();
588 if (node[
"max_backup_files"]) {
589 config.max_backup_files = node[
"max_backup_files"].as<
size_t>();
591 if (node[
"format_pattern"]) {
592 config.format_pattern = node[
"format_pattern"].as<std::string>();
596 static void parse_monitoring_config(
const YAML::Node& node, monitoring_config& config) {
597 if (node[
"enabled"]) {
598 config.enabled = node[
"enabled"].as<
bool>();
600 if (node[
"metrics_interval_ms"]) {
601 config.metrics_interval = std::chrono::milliseconds{node[
"metrics_interval_ms"].as<int64_t>()};
603 if (node[
"health_check_interval_ms"]) {
604 config.health_check_interval = std::chrono::milliseconds{node[
"health_check_interval_ms"].as<int64_t>()};
606 if (node[
"prometheus_port"]) {
607 config.prometheus_port = node[
"prometheus_port"].as<uint16_t>();
609 if (node[
"prometheus_path"]) {
610 config.prometheus_path = node[
"prometheus_path"].as<std::string>();
614 if (node[
"tracing"]) {
615 auto tracing = node[
"tracing"];
616 if (tracing[
"enabled"]) {
617 config.tracing.enabled = tracing[
"enabled"].as<
bool>();
619 if (tracing[
"sampling_rate"]) {
620 config.tracing.sampling_rate = tracing[
"sampling_rate"].as<
double>();
622 if (tracing[
"exporter"]) {
623 config.tracing.exporter = tracing[
"exporter"].as<std::string>();
625 if (tracing[
"endpoint"]) {
626 config.tracing.endpoint = tracing[
"endpoint"].as<std::string>();
631 static void parse_database_config(
const YAML::Node& node, database_config& config) {
632 if (node[
"backend"]) {
633 config.backend = node[
"backend"].as<std::string>();
635 if (node[
"connection_string"]) {
636 config.connection_string = node[
"connection_string"].as<std::string>();
638 if (node[
"log_queries"]) {
639 config.log_queries = node[
"log_queries"].as<
bool>();
641 if (node[
"slow_query_threshold_ms"]) {
642 config.slow_query_threshold = std::chrono::milliseconds{node[
"slow_query_threshold_ms"].as<int64_t>()};
647 auto pool = node[
"pool"];
648 if (pool[
"min_size"]) {
649 config.pool.min_size = pool[
"min_size"].as<
size_t>();
651 if (pool[
"max_size"]) {
652 config.pool.max_size = pool[
"max_size"].as<
size_t>();
654 if (pool[
"idle_timeout_ms"]) {
655 config.pool.idle_timeout = std::chrono::milliseconds{pool[
"idle_timeout_ms"].as<int64_t>()};
657 if (pool[
"acquire_timeout_ms"]) {
658 config.pool.acquire_timeout = std::chrono::milliseconds{pool[
"acquire_timeout_ms"].as<int64_t>()};
663 static void parse_network_config(
const YAML::Node& node, network_config& config) {
664 if (node[
"compression"]) {
665 config.compression = node[
"compression"].as<std::string>();
667 if (node[
"buffer_size"]) {
668 config.buffer_size = node[
"buffer_size"].as<
size_t>();
670 if (node[
"connect_timeout_ms"]) {
671 config.connect_timeout = std::chrono::milliseconds{node[
"connect_timeout_ms"].as<int64_t>()};
673 if (node[
"io_timeout_ms"]) {
674 config.io_timeout = std::chrono::milliseconds{node[
"io_timeout_ms"].as<int64_t>()};
676 if (node[
"keepalive_interval_ms"]) {
677 config.keepalive_interval = std::chrono::milliseconds{node[
"keepalive_interval_ms"].as<int64_t>()};
679 if (node[
"max_connections"]) {
680 config.max_connections = node[
"max_connections"].as<
size_t>();
685 auto tls = node[
"tls"];
686 if (tls[
"enabled"]) {
687 config.tls.enabled = tls[
"enabled"].as<
bool>();
689 if (tls[
"version"]) {
690 config.tls.version = tls[
"version"].as<std::string>();
692 if (tls[
"cert_path"]) {
693 config.tls.cert_path = tls[
"cert_path"].as<std::string>();
695 if (tls[
"key_path"]) {
696 config.tls.key_path = tls[
"key_path"].as<std::string>();
698 if (tls[
"ca_path"]) {
699 config.tls.ca_path = tls[
"ca_path"].as<std::string>();
701 if (tls[
"verify_peer"]) {
702 config.tls.verify_peer = tls[
"verify_peer"].as<
bool>();
712 static const std::vector<std::string> valid_queue_types = {
"mutex",
"lockfree",
"bounded"};
713 if (std::find(valid_queue_types.begin(), valid_queue_types.end(), config.
queue_type) == valid_queue_types.end()) {
714 issues.push_back({
"thread.queue_type",
715 "Invalid queue type: " + config.
queue_type +
". Valid values: mutex, lockfree, bounded",
721 issues.push_back({
"thread.max_queue_size",
"Queue size must be greater than 0",
false});
727 static const std::vector<std::string> valid_levels = {
"trace",
"debug",
"info",
"warn",
"error",
"critical",
"off"};
728 if (std::find(valid_levels.begin(), valid_levels.end(), config.
level) == valid_levels.end()) {
729 issues.push_back({
"logger.level",
730 "Invalid log level: " + config.
level +
". Valid values: trace, debug, info, warn, error, critical, off",
735 static const std::vector<std::string> valid_writers = {
"console",
"file",
"rotating_file",
"network",
"json"};
736 for (
const auto& writer : config.
writers) {
737 if (std::find(valid_writers.begin(), valid_writers.end(), writer) == valid_writers.end()) {
738 issues.push_back({
"logger.writers",
739 "Invalid writer: " + writer +
". Valid values: console, file, rotating_file, network, json",
746 issues.push_back({
"logger.buffer_size",
747 "Buffer size is very small for async logging. Consider using at least 1024 bytes.",
755 issues.push_back({
"monitoring.tracing.sampling_rate",
756 "Sampling rate must be between 0.0 and 1.0",
761 static const std::vector<std::string> valid_exporters = {
"otlp",
"jaeger",
"zipkin",
"console"};
762 if (std::find(valid_exporters.begin(), valid_exporters.end(), config.
tracing.
exporter) == valid_exporters.end()) {
763 issues.push_back({
"monitoring.tracing.exporter",
764 "Invalid exporter: " + config.
tracing.
exporter +
". Valid values: otlp, jaeger, zipkin, console",
770 issues.push_back({
"monitoring.metrics_interval",
771 "Metrics interval is very short (<1s). This may cause performance issues.",
779 static const std::vector<std::string> valid_backends = {
"postgresql",
"mysql",
"sqlite",
"mongodb",
"redis"};
780 if (std::find(valid_backends.begin(), valid_backends.end(), config.
backend) == valid_backends.end()) {
781 issues.push_back({
"database.backend",
782 "Invalid backend: " + config.
backend +
". Valid values: postgresql, mysql, sqlite, mongodb, redis",
789 issues.push_back({
"database.pool",
790 "min_size cannot be greater than max_size",
795 issues.push_back({
"database.pool.max_size",
796 "Pool max_size must be greater than 0",
803 static const std::vector<std::string> valid_compressions = {
"none",
"lz4",
"gzip",
"deflate",
"zstd"};
804 if (std::find(valid_compressions.begin(), valid_compressions.end(), config.
compression) == valid_compressions.end()) {
805 issues.push_back({
"network.compression",
806 "Invalid compression: " + config.
compression +
". Valid values: none, lz4, gzip, deflate, zstd",
811 static const std::vector<std::string> valid_tls_versions = {
"1.2",
"1.3"};
812 if (std::find(valid_tls_versions.begin(), valid_tls_versions.end(), config.
tls.
version) == valid_tls_versions.end()) {
813 issues.push_back({
"network.tls.version",
814 "Invalid TLS version: " + config.
tls.
version +
". Valid values: 1.2, 1.3",
820 issues.push_back({
"network.buffer_size",
821 "Buffer size is very small (<4KB). This may cause performance issues.",
827 issues.push_back({
"network.tls.ca_path",
828 "TLS is enabled with verify_peer but no CA path specified.",
Result type for error handling with member function support.
static Result< T > ok(U &&value)
Create a successful result with value (static factory)
Loads and validates unified configuration from various sources.
static unified_config defaults()
Get default configuration.
static std::string expand_env_vars(const std::string &value)
Expand environment variables in a string.
static VoidResult validate(const unified_config &config)
Validate a configuration.
static Result< unified_config > load_from_string(const std::string &yaml_content)
Load configuration from a YAML string.
static std::vector< validation_issue > get_validation_issues(const unified_config &config)
Get all validation issues for a configuration.
static void apply_env_override_bool(const char *env_name, bool &target)
static void apply_env_override_vector(const char *env_name, std::vector< std::string > &target)
static void apply_env_override(const char *env_name, std::string &target)
static void apply_env_override(const char *env_name, size_t &target)
static Result< unified_config > load_from_env()
Load configuration from environment variables only.
static void validate_logger_config(const logger_config &config, std::vector< validation_issue > &issues)
static Result< unified_config > load(const std::string &path)
Load configuration from a YAML file.
static void validate_database_config(const database_config &config, std::vector< validation_issue > &issues)
static VoidResult merge_env_overrides(unified_config &config)
Apply environment variable overrides to configuration.
static void apply_env_override(const char *env_name, uint16_t &target)
static void validate_monitoring_config(const monitoring_config &config, std::vector< validation_issue > &issues)
static void validate_network_config(const network_config &config, std::vector< validation_issue > &issues)
static void validate_thread_config(const thread_config &config, std::vector< validation_issue > &issues)
static void apply_env_override_ms(const char *env_name, std::chrono::milliseconds &target)
static void apply_env_override_double(const char *env_name, double &target)
constexpr int invalid_value
constexpr int validation_error
constexpr int parse_error
constexpr int file_not_found
Result< T > make_error(int code, const std::string &message, const std::string &module="")
Create an error result with code and message.
Umbrella header for Result<T> type and related utilities.
Database system configuration.
bool log_queries
Enable query logging.
std::string connection_string
Connection string or URI.
std::string backend
Database backend: "postgresql", "mysql", "sqlite", "mongodb", "redis".
pool_config pool
Connection pool configuration.
std::chrono::milliseconds slow_query_threshold
Slow query threshold.
Logging system configuration.
bool async
Enable async logging.
size_t max_file_size
Maximum file size in bytes (for rotating_file)
std::vector< std::string > writers
List of writers: "console", "file", "rotating_file", "network", "json".
std::string file_path
Log file path (for file writers)
size_t max_backup_files
Maximum number of backup files (for rotating_file)
size_t buffer_size
Async buffer size in bytes.
std::string format_pattern
Log format pattern.
std::string level
Log level: "trace", "debug", "info", "warn", "error", "critical", "off".
Monitoring system configuration.
std::chrono::milliseconds metrics_interval
Metrics collection interval.
bool enabled
Enable monitoring.
uint16_t prometheus_port
Prometheus metrics port (0 to disable)
std::chrono::milliseconds health_check_interval
Health check interval.
std::string prometheus_path
Prometheus metrics path.
tracing_config tracing
Tracing configuration.
Network system configuration.
std::chrono::milliseconds connect_timeout
Connection timeout.
size_t max_connections
Maximum concurrent connections (server)
std::chrono::milliseconds io_timeout
Read/write timeout.
size_t buffer_size
Send/receive buffer size.
tls_config tls
TLS configuration.
std::string compression
Compression type: "none", "lz4", "gzip", "deflate", "zstd".
std::chrono::milliseconds keepalive_interval
Keep-alive interval.
size_t max_size
Maximum pool size.
size_t min_size
Minimum pool size.
std::chrono::milliseconds acquire_timeout
Connection acquisition timeout.
std::chrono::milliseconds idle_timeout
Idle connection timeout.
Thread pool configuration.
std::string queue_type
Queue type: "mutex", "lockfree", "bounded".
size_t pool_size
Number of worker threads (default: hardware concurrency)
size_t max_queue_size
Maximum queue size (for bounded queue)
std::string thread_name_prefix
Thread naming prefix.
std::string version
TLS version: "1.2", "1.3".
std::string key_path
Private key file path.
bool verify_peer
Verify peer certificate.
std::string cert_path
Certificate file path.
std::string ca_path
CA certificate path (for client verification)
std::string endpoint
Exporter endpoint.
std::string exporter
Exporter type: "otlp", "jaeger", "zipkin", "console".
bool enabled
Enable tracing.
double sampling_rate
Sampling rate (0.0 to 1.0)
Root configuration structure for the unified system.
network_config network
Network system configuration.
logger_config logger
Logger system configuration.
static unified_config defaults()
Create a configuration with all default values.
thread_config thread
Thread system configuration.
monitoring_config monitoring
Monitoring system configuration.
database_config database
Database system configuration.
Validation result for a configuration field.
Unified configuration schema for the entire system.