81template <
typename Schema, Serializer SerializerT = ConfSerializer,
typename ThreadPolicy = SingleThreadedPolicy>
92 : file_path_(std::move(file_path))
93 , file_values_(
Value::Object())
106 template <IsSetting S>
107 requires(Schema::template has_setting<S>)
108 [[nodiscard]]
auto GetImpl()
const ->
typename S::value_type
110 using value_type =
typename S::value_type;
113 constexpr auto env_override = GetEnvOverride<S>();
114 if constexpr (!env_override.empty()) {
115 if (
const char* env_value = std::getenv(std::string(env_override).c_str())) {
117 if (parsed.has_value()) {
120 Logger::WarnF(
"Failed to parse environment variable %.*s='%s', using fallback",
121 static_cast<int>(env_override.size()), env_override.data(), env_value);
127 typename ThreadPolicy::shared_lock lock(mutex_);
128 auto file_result = file_values_.
GetAtPath(S::path);
129 if (file_result.ok()) {
131 if (parsed.has_value()) {
134 Logger::WarnF(
"Failed to parse file value for '%.*s', using default",
135 static_cast<int>(S::path.size()), S::path.data());
140 return S::default_value();
147 template <IsSetting S>
148 requires(Schema::template has_setting<S>)
151 using value_type =
typename S::value_type;
154 auto validator = GetSettingValidator<S>();
155 auto validation = validator(value);
161 typename ThreadPolicy::unique_lock lock(mutex_);
163 file_values_.
SetAtPath(S::path, serialized);
174 typename ThreadPolicy::unique_lock lock(mutex_);
175 return LoadUnlocked();
184 typename ThreadPolicy::shared_lock lock(mutex_);
185 return SaveUnlocked();
193 typename ThreadPolicy::shared_lock lock(mutex_);
202 typename ThreadPolicy::shared_lock lock(mutex_);
203 return ValidateAllUnlocked();
235 [[nodiscard]]
auto LoadUnlocked() ->
Status
237 namespace fs = std::filesystem;
239 if (!fs::exists(file_path_)) {
241 Logger::InfoF(
"Configuration file '%s' not found, creating with defaults", file_path_.c_str());
242 file_values_ = defaults_;
243 return SaveUnlocked();
247 auto result = ReadFile<SerializerT>(file_path_);
249 return result.status();
252 file_values_ = *result;
256 auto added = diff.Added();
258 if (!added.empty()) {
259 Logger::Warn(
"New settings detected in schema, adding to configuration file:");
260 for (
const auto& entry : added) {
261 Logger::WarnF(
" - %s = %s", entry.path.c_str(), entry.new_value.c_str());
263 auto default_val = defaults_.
GetAtPath(entry.path);
264 if (default_val.ok()) {
265 file_values_.
SetAtPath(entry.path, *default_val);
270 auto save_status = SaveUnlocked();
271 if (!save_status.ok()) {
273 std::string(save_status.message()).c_str());
282 [[nodiscard]]
auto SaveUnlocked() const -> Status
284 namespace fs = std::filesystem;
287 fs::path path(file_path_);
288 if (path.has_parent_path()) {
289 std::error_code error_code;
290 fs::create_directories(path.parent_path(), error_code);
292 return InternalError(
"Failed to create directory: " + error_code.message());
296 return WriteFile<SerializerT>(file_path_, file_values_);
300 [[nodiscard]]
auto ValidateAllUnlocked() const -> Status
304 Schema::ForEachSetting([
this, &status]<
typename S>() {
309 using value_type =
typename S::value_type;
310 auto file_result = file_values_.
GetAtPath(S::path);
312 if (file_result.ok()) {
314 if (parsed.has_value()) {
315 auto validator = GetSettingValidator<S>();
316 auto validation = validator(*parsed);
331 Schema::ForEachSetting([
this]<
typename S>() {
332 using value_type =
typename S::value_type;
334 defaults_.
SetAtPath(S::path, serialized);
338 std::string file_path_;
341 mutable typename ThreadPolicy::mutex_type mutex_;
Result of comparing two configurations.
Definition diff.h:39
Main configuration manager.
Definition configuration.h:83
auto GetFileValues() const -> const Value &
Returns the current file values.
Definition configuration.h:215
auto Load() -> Status override
Loads configuration from the file.
Definition configuration.h:223
auto GetImpl() const -> typename S::value_type
Gets the value for a setting type.
Definition configuration.h:108
auto SetImpl(typename S::value_type value) -> Status
Sets the value for a setting type.
Definition configuration.h:149
Configuration(std::string file_path)
Creates a configuration manager with a file path.
Definition configuration.h:91
auto LoadImpl() -> Status
Loads configuration from the file.
Definition configuration.h:172
SerializerT serializer_type
Definition configuration.h:85
auto GetDefaults() const -> const Value &
Returns the default values.
Definition configuration.h:221
auto GetFilePathImpl() const -> std::string_view
Returns the file path.
Definition configuration.h:209
auto GetDiffString() const -> std::string override
Gets a string representation of the diff.
Definition configuration.h:231
auto DiffImpl() const -> ConfigDiff
Returns the diff between file values and defaults.
Definition configuration.h:191
auto ValidateAll() const -> Status override
Validates all current values.
Definition configuration.h:229
auto GetFilePath() const -> std::string_view override
Returns the file path.
Definition configuration.h:227
auto Save() const -> Status override
Saves the current configuration to the file.
Definition configuration.h:225
auto SaveImpl() const -> Status
Saves the current configuration to the file.
Definition configuration.h:182
auto ValidateAllImpl() const -> Status
Validates all current values against their validators.
Definition configuration.h:200
Virtual interface for type-erased configuration access.
Definition interface.h:89
CRTP base class for configuration providers.
Definition interface.h:20
static void WarnF(const char *format, Args... args)
Logs a formatted warning message to stderr.
Definition logging.h:62
static void ErrorF(const char *format, Args... args)
Logs a formatted error message to stderr.
Definition logging.h:74
static void InfoF(const char *format, Args... args)
Logs a formatted info message to stdout.
Definition logging.h:50
static void Warn(std::string_view message)
Logs a warning message to stderr.
Definition logging.h:21
A lightweight status object carrying an error code and message.
Definition status.h:23
A self-contained, recursive value type for configuration data.
Definition value.h:26
auto GetAtPath(std::string_view path) const -> StatusOr< Value >
Gets a value at a dot-separated path.
Definition value.h:265
void SetAtPath(std::string_view path, const Value &value)
Sets a value at a dot-separated path, creating intermediate objects.
Definition value.h:285
static auto Object() -> Value
Creates an empty object value.
Definition value.h:133
C++20 compile-time type-safe configuration library.
Definition conf.h:13
auto DiffDefaultsFromFile(const Value &defaults, const Value &file_values) -> ConfigDiff
Compares defaults against file configuration.
Definition diff.h:168
auto InternalError(std::string message) -> Status
Returns an Internal error status.
Definition status.h:67
auto DiffFileFromDefaults(const Value &defaults, const Value &file_values) -> ConfigDiff
Compares file configuration against defaults.
Definition diff.h:157
auto OkStatus() -> Status
Returns an OK status.
Definition status.h:52
auto InvalidArgumentError(std::string message) -> Status
Returns an InvalidArgument error status.
Definition status.h:61
static auto Deserialize(const Value &value) -> std::optional< T >=delete
Deserializes a value from a Value node.
static auto FromString(std::string_view str) -> std::optional< T >=delete
Parses a value from a string (e.g., from environment variables).
static auto Serialize(const T &value) -> Value=delete
Serializes a value to a Value node.