new(userspace,unit_tests)!: add a way to specify merge-strategy for config_files.

By default we now use the `append` merge-strategy:
* existing sequence keys will be appended
* existing scalar keys will be overridden
* non-existing keys will be added

We also have an `override` merge-strategy:
* existing keys will be overridden
* non-existing keys will be added

Finally, there is an `add-only` merge-strategy:
* existing keys will be ignored
* non-existing keys will be added

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
This commit is contained in:
Federico Di Pierro
2025-04-24 12:12:25 +02:00
committed by poiana
parent 80d52963d6
commit 630167d9ad
4 changed files with 250 additions and 8 deletions

View File

@@ -85,6 +85,27 @@ public:
inline static const std::string validation_failed = "failed";
inline static const std::string validation_none = "none";
enum include_files_strategy {
STRATEGY_APPEND, // append to existing sequence keys, override scalar keys and add new ones
STRATEGY_OVERRIDE, // override existing keys (sequences too) and add new ones
STRATEGY_ADDONLY // only add new keys and ignore existing ones
};
static inline enum include_files_strategy get_include_file_strategy(
std::string& include_file_name) {
if(include_file_name.length() > 0) {
if(include_file_name[0] == '+') {
include_file_name.erase(0, 1);
return STRATEGY_ADDONLY;
}
if(include_file_name[0] == '@') {
include_file_name.erase(0, 1);
return STRATEGY_OVERRIDE;
}
}
return STRATEGY_APPEND;
}
/**
* Load all the YAML document represented by the input string.
* Since this is used by rule loader, does not process env vars.
@@ -137,6 +158,7 @@ public:
}
void include_config_file(const std::string& include_file_path,
enum include_files_strategy strategy = STRATEGY_APPEND,
const nlohmann::json& schema = {},
std::vector<std::string>* schema_warnings = nullptr) {
auto loaded_nodes = load_from_file_int(include_file_path, schema, schema_warnings);
@@ -152,10 +174,24 @@ public:
"' directive in included config file " +
include_file_path + ".");
}
// We allow to override keys.
// We don't need to use `get_node()` here,
// since key is a top-level one.
m_root[key] = n.second;
switch(strategy) {
case STRATEGY_APPEND:
if(n.second.IsSequence()) {
for(const auto& item : n.second) {
m_root[key].push_back(item);
}
break;
}
// fallthrough
case STRATEGY_OVERRIDE:
m_root[key] = n.second;
break;
case STRATEGY_ADDONLY:
if(!m_root[key].IsDefined()) {
m_root[key] = n.second;
}
break;
}
}
}