#ifndef _UTILS_CONFIG_HPP_ #define _UTILS_CONFIG_HPP_ #include #include #include #include #include #include "exception.hpp" #include "services/logger.hpp" #include "utils/string.hpp" namespace config { class ConfigException : public Exception { using Exception::Exception; }; class UnexistingFileError : public ConfigException { using ConfigException::ConfigException; }; class ParseError : public ConfigException { using ConfigException::ConfigException; }; class MissingValueException : public ConfigException { using ConfigException::ConfigException; }; class MissingListValueException : public ConfigException { using ConfigException::ConfigException; }; class InvalidVariableException : public ConfigException { using ConfigException::ConfigException; }; class InvalidReferenceException : public ConfigException { using ConfigException::ConfigException; }; static std::recursive_mutex mtx; std::string get_bar_path(); void set_bar_path(const std::string& path); void load(const std::string& path) throw(UnexistingFileError, ParseError); void load(const char *dir, const std::string& path); void reload() throw(ParseError); boost::property_tree::ptree get_tree(); std::string build_path(const std::string& section, const std::string& key); template T dereference_var(const std::string& ref_section, const std::string& ref_key, const std::string& var, const T ref_val) throw (InvalidVariableException, InvalidReferenceException) { std::lock_guard lck(config::mtx); std::string::size_type n, m; if ((n = var.find("${")) != 0) return ref_val; if ((m = var.find("}")) != var.length()-1) return ref_val; auto path = var.substr(2, m-2); auto ref_path = build_path(ref_section, ref_key); if ((n = path.find(".")) == std::string::npos) throw InvalidVariableException("Invalid variable "+ ref_path +" => ${"+ path +"}"); auto section = string::replace(path.substr(0, n), "BAR", get_bar_path()); auto key = path.substr(n+1, path.length()-n-1); auto val = get_tree().get_optional(build_path(section, key)); if (val == boost::none) throw InvalidReferenceException("Variable defined at ["+ ref_path +"] points to a non existing parameter: ["+ build_path(section, key) +"]"); auto str_val = get_tree().get(build_path(section, key)); return dereference_var(section, key, str_val, val.get()); } template T get(const std::string& section, const std::string& key) throw (MissingValueException) { std::lock_guard lck(config::mtx); auto val = get_tree().get_optional(build_path(section, key)); if (val == boost::none) throw MissingValueException("Missing property \""+ key +"\" in section ["+ section +"]"); auto str_val = get_tree().get(build_path(section, key)); return dereference_var(section, key, str_val, val.get()); } template T get(const std::string& section, const std::string& key, T default_value) { std::lock_guard lck(config::mtx); auto val = get_tree().get_optional(build_path(section, key)); auto str_val = get_tree().get_optional(build_path(section, key)); return dereference_var(section, key, str_val.get_value_or(""), val.get_value_or(default_value)); } template std::vector get_list(const std::string& section, const std::string& key) throw (MissingListValueException) { std::lock_guard lck(config::mtx); std::vector vec; boost::optional value; while ((value = get_tree().get_optional(build_path(section, key) + ":"+ std::to_string(vec.size()))) != boost::none) { auto str_val = get_tree().get(build_path(section, key) + ":"+ std::to_string(vec.size())); vec.emplace_back(dereference_var(section, key, str_val, value.get())); } if (vec.empty()) throw MissingListValueException("Missing property \""+ key + ":0\" in section ["+ section +"]"); return vec; } template std::vector get_list(const std::string& section, const std::string& key, std::vector default_value) { std::lock_guard lck(config::mtx); std::vector vec; boost::optional value; while ((value = get_tree().get_optional(build_path(section, key) + ":"+ std::to_string(vec.size()))) != boost::none) { auto str_val = get_tree().get(build_path(section, key) + ":"+ std::to_string(vec.size())); vec.emplace_back(dereference_var(section, key, str_val, value.get())); } if (vec.empty()) return default_value; else return vec; } } #endif