refactor(config): more sensible multi-bar include behavior

pull/1243/head
Aleksei Bavshin 2021-09-14 12:16:37 +07:00
parent 8912bd3ed0
commit ccc60b4245
No known key found for this signature in database
GPG Key ID: 4F071603387A382A
8 changed files with 28 additions and 58 deletions

View File

@ -28,7 +28,7 @@ class Config {
std::vector<Json::Value> getOutputConfigs(const std::string &name, const std::string &identifier); std::vector<Json::Value> getOutputConfigs(const std::string &name, const std::string &identifier);
private: private:
void setupConfig(const std::string &config_file, int depth); void setupConfig(Json::Value &dst, const std::string &config_file, int depth);
void resolveConfigIncludes(Json::Value &config, int depth); void resolveConfigIncludes(Json::Value &config, int depth);
void mergeConfig(Json::Value &a_config_, Json::Value &b_config_); void mergeConfig(Json::Value &a_config_, Json::Value &b_config_);

View File

@ -46,7 +46,7 @@ std::optional<std::string> Config::findConfigPath(const std::vector<std::string>
return std::nullopt; return std::nullopt;
} }
void Config::setupConfig(const std::string &config_file, int depth) { void Config::setupConfig(Json::Value &dst, const std::string &config_file, int depth) {
if (depth > 100) { if (depth > 100) {
throw std::runtime_error("Aborting due to likely recursive include in config files"); throw std::runtime_error("Aborting due to likely recursive include in config files");
} }
@ -64,7 +64,7 @@ void Config::setupConfig(const std::string &config_file, int depth) {
} else { } else {
resolveConfigIncludes(tmp_config, depth); resolveConfigIncludes(tmp_config, depth);
} }
mergeConfig(config_, tmp_config); mergeConfig(dst, tmp_config);
} }
void Config::resolveConfigIncludes(Json::Value &config, int depth) { void Config::resolveConfigIncludes(Json::Value &config, int depth) {
@ -72,11 +72,11 @@ void Config::resolveConfigIncludes(Json::Value &config, int depth) {
if (includes.isArray()) { if (includes.isArray()) {
for (const auto &include : includes) { for (const auto &include : includes) {
spdlog::info("Including resource file: {}", include.asString()); spdlog::info("Including resource file: {}", include.asString());
setupConfig(tryExpandPath(include.asString()).value_or(""), ++depth); setupConfig(config, tryExpandPath(include.asString()).value_or(""), ++depth);
} }
} else if (includes.isString()) { } else if (includes.isString()) {
spdlog::info("Including resource file: {}", includes.asString()); spdlog::info("Including resource file: {}", includes.asString());
setupConfig(tryExpandPath(includes.asString()).value_or(""), ++depth); setupConfig(config, tryExpandPath(includes.asString()).value_or(""), ++depth);
} }
} }
@ -88,17 +88,11 @@ void Config::mergeConfig(Json::Value &a_config_, Json::Value &b_config_) {
for (const auto &key : b_config_.getMemberNames()) { for (const auto &key : b_config_.getMemberNames()) {
if (a_config_[key].isObject() && b_config_[key].isObject()) { if (a_config_[key].isObject() && b_config_[key].isObject()) {
mergeConfig(a_config_[key], b_config_[key]); mergeConfig(a_config_[key], b_config_[key]);
} else { } else if (a_config_[key].isNull()) {
// do not allow overriding value set by top or previously included config
a_config_[key] = b_config_[key]; a_config_[key] = b_config_[key];
} }
} }
} else if (a_config_.isArray() && b_config_.isArray()) {
// This can happen only on the top-level array of a multi-bar config
for (Json::Value::ArrayIndex i = 0; i < b_config_.size(); i++) {
if (a_config_[i].isObject() && b_config_[i].isObject()) {
mergeConfig(a_config_[i], b_config_[i]);
}
}
} else { } else {
spdlog::error("Cannot merge config, conflicting or invalid JSON types"); spdlog::error("Cannot merge config, conflicting or invalid JSON types");
} }
@ -133,7 +127,7 @@ void Config::load(const std::string &config) {
} }
config_file_ = file.value(); config_file_ = file.value();
spdlog::info("Using configuration file {}", config_file_); spdlog::info("Using configuration file {}", config_file_);
setupConfig(config_file_, 0); setupConfig(config_, config_file_, 0);
} }
std::vector<Json::Value> Config::getOutputConfigs(const std::string &name, std::vector<Json::Value> Config::getOutputConfigs(const std::string &name,

View File

@ -62,7 +62,8 @@ TEST_CASE("Load simple config with include", "[config]") {
SECTION("validate the config data") { SECTION("validate the config data") {
auto& data = conf.getConfig(); auto& data = conf.getConfig();
REQUIRE(data["layer"].asString() == "bottom"); // config override behavior: preserve first included value
REQUIRE(data["layer"].asString() == "top");
REQUIRE(data["height"].asInt() == 30); REQUIRE(data["height"].asInt() == 30);
// config override behavior: preserve value from the top config // config override behavior: preserve value from the top config
REQUIRE(data["position"].asString() == "top"); REQUIRE(data["position"].asString() == "top");

View File

@ -1,9 +1,4 @@
[ {
{ "output": "OUT-0",
"output": "OUT-0", "height": 20
"height": 20 }
},
{},
{},
{}
]

View File

@ -1,8 +1,3 @@
[ {
{}, "height": 21
{ }
"height": 21
},
{},
{}
]

View File

@ -1,9 +1,4 @@
[ {
{}, "output": "OUT-1",
{}, "height": 22
{ }
"output": "OUT-1",
"height": 22
},
{}
]

View File

@ -1,8 +1,3 @@
[ {
{}, "height": 23
{}, }
{},
{
"height": 23
}
]

View File

@ -1,9 +1,4 @@
[ {
{}, "output": "OUT-3",
{}, "include": "test/config/include-multi-3-0.json"
{}, }
{
"output": "OUT-3",
"include": "test/config/include-multi-3-0.json"
}
]