25 #include <unordered_map>
40 if (it->second.validator) {
41 return it->second.validator(value);
47 if (value.type() ==
typeid(std::string)) {
49 }
else if (value.type() ==
typeid(
int)) {
51 }
else if (value.type() ==
typeid(
bool)) {
53 }
else if (value.type() ==
typeid(
double)) {
57 if (expected_type != actual_type) {
64 void notify_change(
const std::string& key,
const std::any& old_value,
const std::any& new_value) {
68 it->second(key, old_value, new_value);
69 }
catch (
const std::exception& e) {
71 "Error in change callback for key '" + key +
"': " + std::string(e.what()));
80 return std::any_cast<std::string>(value);
84 return std::any_cast<bool>(value) ?
"true" :
"false";
90 }
catch (
const std::bad_any_cast&) {
99 return std::any(value_str);
101 return std::any(std::stoi(value_str));
103 return std::any(value_str ==
"true");
105 return std::any(std::stod(value_str));
107 return std::any(value_str);
109 }
catch (
const std::exception&) {
110 return std::any(value_str);
122 std::lock_guard<std::mutex> lock(get_impl()->mutex_);
123 auto it = get_impl()->config_items_.find(key);
124 if (it != get_impl()->config_items_.end()) {
125 return it->second.value;
127 throw std::runtime_error(
"Configuration key not found: " + key);
131 std::lock_guard<std::mutex> lock(get_impl()->mutex_);
133 if (it != get_impl()->config_items_.end()) {
134 return it->second.value;
136 return default_value;
140 std::lock_guard<std::mutex> lock(get_impl()->mutex_);
145 std::lock_guard<std::mutex> lock(impl_->mutex_);
147 auto validation_result = impl_->validate_value(key, value);
148 if (!validation_result.is_valid) {
149 return validation_result;
153 bool had_key =
false;
154 auto it = impl_->config_items_.find(key);
155 if (it != impl_->config_items_.end()) {
156 old_value = it->second.value;
158 it->second.value = value;
161 impl_->config_items_[key] = item;
165 impl_->notify_change(key, old_value, value);
172 std::lock_guard<std::mutex> lock(impl_->mutex_);
173 auto it = impl_->config_items_.find(key);
174 if (it != impl_->config_items_.end()) {
175 impl_->config_items_.erase(it);
182 std::lock_guard<std::mutex> lock(impl_->mutex_);
183 impl_->config_items_.clear();
187 std::lock_guard<std::mutex> lock(get_impl()->mutex_);
189 for (
const auto& [key, item] : get_impl()->config_items_) {
191 if (!result.is_valid) {
200 std::lock_guard<std::mutex> lock(get_impl()->mutex_);
202 if (it == get_impl()->config_items_.end()) {
210 std::lock_guard<std::mutex> lock(impl_->mutex_);
211 impl_->config_items_[item.
key] = item;
216 std::lock_guard<std::mutex> lock(impl_->mutex_);
217 auto it = impl_->config_items_.find(key);
218 if (it != impl_->config_items_.end()) {
219 it->second.validator = validator;
224 std::lock_guard<std::mutex> lock(impl_->mutex_);
225 impl_->change_callbacks_[key] = callback;
229 std::lock_guard<std::mutex> lock(impl_->mutex_);
230 impl_->change_callbacks_.erase(key);
234 std::lock_guard<std::mutex> lock(get_impl()->mutex_);
237 std::ofstream file(filepath);
238 if (!file.is_open()) {
242 file <<
"# unilink configuration file\n";
243 file <<
"# Generated automatically\n\n";
245 for (
const auto& [key, item] : get_impl()->config_items_) {
246 file <<
"# " << item.description <<
"\n";
247 file << key <<
"=" << get_impl()->
serialize_value(item.value, item.type) <<
"\n\n";
251 }
catch (
const std::exception& e) {
252 UNILINK_LOG_ERROR(
"config_manager",
"save",
"Error saving configuration: " + std::string(e.what()));
258 std::lock_guard<std::mutex> lock(impl_->mutex_);
261 std::ifstream file(filepath);
262 if (!file.is_open()) {
267 while (std::getline(file, line)) {
268 if (line.empty() || line[0] ==
'#') {
272 size_t pos = line.find(
'=');
273 if (pos != std::string::npos) {
274 std::string key = line.substr(0, pos);
275 std::string value_str = line.substr(pos + 1);
277 key.erase(0, key.find_first_not_of(
" \t"));
278 key.erase(key.find_last_not_of(
" \t") + 1);
279 value_str.erase(0, value_str.find_first_not_of(
" \t"));
280 value_str.erase(value_str.find_last_not_of(
" \t") + 1);
283 auto it = impl_->config_items_.find(key);
284 bool exists = (it != impl_->config_items_.end());
287 type = it->second.type;
289 if (value_str ==
"true" || value_str ==
"false") {
291 }
else if (std::all_of(value_str.begin(), value_str.end(),
292 [](
char c) { return std::isdigit(c) || c ==
'-'; })) {
294 }
else if (std::count(value_str.begin(), value_str.end(),
'.') == 1 &&
295 std::all_of(value_str.begin(), value_str.end(),
296 [](
char c) { return std::isdigit(c) || c ==
'.' || c ==
'-'; })) {
301 std::any value = impl_->deserialize_value(value_str, type);
304 auto result = impl_->validate_value(key, value);
305 if (!result.is_valid) {
307 "Validation failed for key '" + key +
"': " + result.error_message);
311 std::any old_value = it->second.value;
312 it->second.value = value;
313 impl_->notify_change(key, old_value, value);
316 impl_->config_items_[key] = item;
322 }
catch (
const std::exception& e) {
323 UNILINK_LOG_ERROR(
"config_manager",
"load",
"Error loading configuration: " + std::string(e.what()));
329 std::lock_guard<std::mutex> lock(get_impl()->mutex_);
330 std::vector<std::string> keys;
331 keys.reserve(get_impl()->config_items_.size());
333 for (
const auto& [key, item] : get_impl()->config_items_) {
341 std::lock_guard<std::mutex> lock(get_impl()->mutex_);
343 if (it != get_impl()->config_items_.end()) {
344 return it->second.type;
346 throw std::runtime_error(
"Configuration key not found: " + key);
350 std::lock_guard<std::mutex> lock(get_impl()->mutex_);
352 if (it != get_impl()->config_items_.end()) {
353 return it->second.description;
359 std::lock_guard<std::mutex> lock(get_impl()->mutex_);
361 if (it != get_impl()->config_items_.end()) {
362 return it->second.required;
std::string get_description(const std::string &key) const override
bool has(const std::string &key) const override
std::any get(const std::string &key) const override
ValidationResult validate() const override
bool load_from_file(const std::string &filepath) override
void remove_change_callback(const std::string &key) override
bool remove(const std::string &key) override
bool save_to_file(const std::string &filepath) const override
void on_change(const std::string &key, ConfigChangeCallback callback) override
~ConfigManager() override
void register_item(const ConfigItem &item) override
std::vector< std::string > get_keys() const override
bool is_required(const std::string &key) const override
void register_validator(const std::string &key, std::function< ValidationResult(const std::any &)> validator) override
ValidationResult set(const std::string &key, const std::any &value) override
ConfigType get_type(const std::string &key) const override
#define UNILINK_LOG_ERROR(component, operation, message)
std::function< void(const std::string &key, const std::any &old_value, const std::any &new_value)> ConfigChangeCallback
std::string to_string(ErrorCode code)
Convert ErrorCode to human-readable string.
std::unordered_map< std::string, ConfigItem > config_items_
std::string serialize_value(const std::any &value, ConfigType type) const
std::any deserialize_value(const std::string &value_str, ConfigType type) const
void notify_change(const std::string &key, const std::any &old_value, const std::any &new_value)
ValidationResult validate_value(const std::string &key, const std::any &value) const
std::unordered_map< std::string, ConfigChangeCallback > change_callbacks_
static ValidationResult success()
static ValidationResult error(const std::string &msg)