Add match merging with parent config. Fix #16. Fix #17

This commit is contained in:
Federico Terzi 2019-09-14 22:14:39 +02:00
parent 7a679c6395
commit b40778cc84
2 changed files with 117 additions and 1 deletions

View File

@ -29,6 +29,7 @@ fn default_log_level() -> i32 { 0 }
fn default_config_caching_interval() -> i32 { 800 } fn default_config_caching_interval() -> i32 { 800 }
fn default_toggle_interval() -> u32 { 230 } fn default_toggle_interval() -> u32 { 230 }
fn default_backspace_limit() -> i32 { 3 } fn default_backspace_limit() -> i32 { 3 }
fn default_exclude_parent_matches() -> bool {false}
fn default_matches() -> Vec<Match> { Vec::new() } fn default_matches() -> Vec<Match> { Vec::new() }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
@ -66,6 +67,9 @@ pub struct Configs {
#[serde(default)] #[serde(default)]
pub backend: BackendType, pub backend: BackendType,
#[serde(default = "default_exclude_parent_matches")]
pub exclude_parent_matches: bool,
#[serde(default = "default_matches")] #[serde(default = "default_matches")]
pub matches: Vec<Match> pub matches: Vec<Match>
} }
@ -176,7 +180,7 @@ impl ConfigSet {
continue; continue;
} }
let config = Configs::load_config(path.as_path())?; let mut config = Configs::load_config(path.as_path())?;
if !config.validate_specific_config() { if !config.validate_specific_config() {
return Err(ConfigLoadError::InvalidParameter(path.to_owned())) return Err(ConfigLoadError::InvalidParameter(path.to_owned()))
@ -190,6 +194,23 @@ impl ConfigSet {
return Err(ConfigLoadError::NameDuplicate(path.to_owned())); return Err(ConfigLoadError::NameDuplicate(path.to_owned()));
} }
// Compute new match set, merging the parent's matches.
// Note: if an app-specific redefines a trigger already present in the
// default config, the latter gets overwritten.
if !config.exclude_parent_matches {
let mut merged_matches = config.matches.clone();
let mut trigger_set = HashSet::new();
merged_matches.iter().for_each(|m| {
trigger_set.insert(m.trigger.clone());
});
let parent_matches : Vec<Match> = default.matches.iter().filter(|&m| {
!trigger_set.contains(&m.trigger)
}).map(|m| m.clone()).collect();
merged_matches.extend(parent_matches);
config.matches = merged_matches;
}
// TODO: check if it contains at least a filter, and warn the user about the problem // TODO: check if it contains at least a filter, and warn the user about the problem
name_set.insert(config.name.clone()); name_set.insert(config.name.clone());
@ -521,4 +542,96 @@ mod tests {
assert!(config_set.is_err()); assert!(config_set.is_err());
assert!(variant_eq(&config_set.unwrap_err(), &ConfigLoadError::NameDuplicate(PathBuf::new()))) assert!(variant_eq(&config_set.unwrap_err(), &ConfigLoadError::NameDuplicate(PathBuf::new())))
} }
#[test]
fn test_specific_config_set_merge_with_parent_matches() {
let tmp_dir = TempDir::new().expect("unable to create temp directory");
let default_path = tmp_dir.path().join(DEFAULT_CONFIG_FILE_NAME);
fs::write(default_path, r###"
matches:
- trigger: ":lol"
replace: "LOL"
- trigger: ":yess"
replace: "Bob"
"###);
let specific_path = tmp_dir.path().join("specific.yaml");
let specific_path_copy = specific_path.clone();
fs::write(specific_path, r###"
name: specific1
matches:
- trigger: "hello"
replace: "newstring"
"###);
let config_set = ConfigSet::load(tmp_dir.path()).unwrap();
assert_eq!(config_set.default.matches.len(), 2);
assert_eq!(config_set.specific[0].matches.len(), 3);
assert!(config_set.specific[0].matches.iter().find(|x| x.trigger == "hello").is_some());
assert!(config_set.specific[0].matches.iter().find(|x| x.trigger == ":lol").is_some());
assert!(config_set.specific[0].matches.iter().find(|x| x.trigger == ":yess").is_some());
}
#[test]
fn test_specific_config_set_merge_with_parent_matches_child_priority() {
let tmp_dir = TempDir::new().expect("unable to create temp directory");
let default_path = tmp_dir.path().join(DEFAULT_CONFIG_FILE_NAME);
fs::write(default_path, r###"
matches:
- trigger: ":lol"
replace: "LOL"
- trigger: ":yess"
replace: "Bob"
"###);
let specific_path = tmp_dir.path().join("specific.yaml");
let specific_path_copy = specific_path.clone();
fs::write(specific_path, r###"
name: specific1
matches:
- trigger: ":lol"
replace: "newstring"
"###);
let config_set = ConfigSet::load(tmp_dir.path()).unwrap();
assert_eq!(config_set.default.matches.len(), 2);
assert_eq!(config_set.specific[0].matches.len(), 2);
assert!(config_set.specific[0].matches.iter().find(|x| x.trigger == ":lol" && x.replace == "newstring").is_some());
assert!(config_set.specific[0].matches.iter().find(|x| x.trigger == ":yess").is_some());
}
#[test]
fn test_specific_config_set_exclude_merge_with_parent_matches() {
let tmp_dir = TempDir::new().expect("unable to create temp directory");
let default_path = tmp_dir.path().join(DEFAULT_CONFIG_FILE_NAME);
fs::write(default_path, r###"
matches:
- trigger: ":lol"
replace: "LOL"
- trigger: ":yess"
replace: "Bob"
"###);
let specific_path = tmp_dir.path().join("specific.yaml");
let specific_path_copy = specific_path.clone();
fs::write(specific_path, r###"
name: specific1
exclude_parent_matches: true
matches:
- trigger: "hello"
replace: "newstring"
"###);
let config_set = ConfigSet::load(tmp_dir.path()).unwrap();
assert_eq!(config_set.default.matches.len(), 2);
assert_eq!(config_set.specific[0].matches.len(), 1);
assert!(config_set.specific[0].matches.iter().find(|x| x.trigger == "hello" && x.replace == "newstring").is_some());
}
} }

View File

@ -8,6 +8,8 @@ use crate::matcher::Match;
pub struct RuntimeConfigManager<'a, S: SystemManager> { pub struct RuntimeConfigManager<'a, S: SystemManager> {
set: ConfigSet, set: ConfigSet,
// Filter regexps
title_regexps: Vec<Option<Regex>>, title_regexps: Vec<Option<Regex>>,
class_regexps: Vec<Option<Regex>>, class_regexps: Vec<Option<Regex>>,
exec_regexps: Vec<Option<Regex>>, exec_regexps: Vec<Option<Regex>>,
@ -178,6 +180,7 @@ impl <'a, S: SystemManager> super::ConfigManager<'a> for RuntimeConfigManager<'a
} }
} }
// TESTS
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {