Refactored configuration file structure and add parent hierarchy
This commit is contained in:
parent
efc6a9c860
commit
c9ecdabb25
30
Cargo.lock
generated
30
Cargo.lock
generated
|
@ -241,6 +241,7 @@ dependencies = [
|
|||
"serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"simplelog 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"widestring 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"zip 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
@ -544,6 +545,14 @@ name = "ryu"
|
|||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.99"
|
||||
|
@ -697,6 +706,16 @@ name = "vec_map"
|
|||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.7.0"
|
||||
|
@ -721,6 +740,14 @@ name = "winapi-i686-pc-windows-gnu"
|
|||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
|
@ -814,6 +841,7 @@ dependencies = [
|
|||
"checksum rust-argon2 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ca4eaef519b494d1f2848fc602d18816fed808a981aedf4f1f00ceb7c9d32cf"
|
||||
"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
|
||||
"checksum ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997"
|
||||
"checksum same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421"
|
||||
"checksum serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)" = "fec2851eb56d010dc9a21b89ca53ee75e6528bab60c11e89d38390904982da9f"
|
||||
"checksum serde_derive 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)" = "cb4dc18c61206b08dc98216c98faa0232f4337e1e1b8574551d5bad29ea1b425"
|
||||
"checksum serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)" = "051c49229f282f7c6f3813f8286cc1e3323e8051823fce42c7ea80fe13521704"
|
||||
|
@ -832,10 +860,12 @@ dependencies = [
|
|||
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
||||
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
||||
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
|
||||
"checksum walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9658c94fa8b940eab2250bd5a457f9c48b748420d71293b165c8cdbe2f55f71e"
|
||||
"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d"
|
||||
"checksum widestring 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "effc0e4ff8085673ea7b9b2e3c73f6bd4d118810c9009ed8f1e16bd96c331db6"
|
||||
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
|
||||
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
"checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9"
|
||||
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
"checksum yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d"
|
||||
"checksum zip 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3c21bb410afa2bd823a047f5bda3adb62f51074ac7e06263b2c97ecdd47e9fc6"
|
||||
|
|
|
@ -25,6 +25,7 @@ log-panics = {version = "2.0.0", features = ["with-backtrace"]}
|
|||
backtrace = "0.3.37"
|
||||
chrono = "0.4.9"
|
||||
lazy_static = "1.4.0"
|
||||
walkdir = "2.2.9"
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libc = "0.2.62"
|
||||
|
|
|
@ -22,14 +22,15 @@ extern crate dirs;
|
|||
use std::path::{Path, PathBuf};
|
||||
use std::{fs};
|
||||
use crate::matcher::Match;
|
||||
use std::fs::{File, create_dir_all};
|
||||
use std::fs::{File, create_dir_all, DirEntry};
|
||||
use std::io::Read;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use crate::event::KeyModifier;
|
||||
use std::collections::HashSet;
|
||||
use std::collections::{HashSet, HashMap};
|
||||
use log::{error};
|
||||
use std::fmt;
|
||||
use std::error::Error;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
pub(crate) mod runtime;
|
||||
|
||||
|
@ -42,6 +43,7 @@ const PACKAGES_FOLDER_NAME : &str = "packages";
|
|||
|
||||
// Default values for primitives
|
||||
fn default_name() -> String{ "default".to_owned() }
|
||||
fn default_parent() -> String{ "self".to_owned() }
|
||||
fn default_filter_title() -> String{ "".to_owned() }
|
||||
fn default_filter_class() -> String{ "".to_owned() }
|
||||
fn default_filter_exec() -> String{ "".to_owned() }
|
||||
|
@ -52,7 +54,7 @@ fn default_use_system_agent() -> bool { true }
|
|||
fn default_config_caching_interval() -> i32 { 800 }
|
||||
fn default_toggle_interval() -> u32 { 230 }
|
||||
fn default_backspace_limit() -> i32 { 3 }
|
||||
fn default_exclude_parent_matches() -> bool {false}
|
||||
fn default_exclude_default_matches() -> bool {false}
|
||||
fn default_matches() -> Vec<Match> { Vec::new() }
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
|
@ -60,6 +62,9 @@ pub struct Configs {
|
|||
#[serde(default = "default_name")]
|
||||
pub name: String,
|
||||
|
||||
#[serde(default = "default_parent")]
|
||||
pub parent: String,
|
||||
|
||||
#[serde(default = "default_filter_title")]
|
||||
pub filter_title: String,
|
||||
|
||||
|
@ -96,8 +101,8 @@ pub struct Configs {
|
|||
#[serde(default)]
|
||||
pub backend: BackendType,
|
||||
|
||||
#[serde(default = "default_exclude_parent_matches")]
|
||||
pub exclude_parent_matches: bool,
|
||||
#[serde(default = "default_exclude_default_matches")]
|
||||
pub exclude_default_matches: bool,
|
||||
|
||||
#[serde(default = "default_matches")]
|
||||
pub matches: Vec<Match>
|
||||
|
@ -173,6 +178,32 @@ impl Configs {
|
|||
Err(ConfigLoadError::FileNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
fn merge_config(&mut self, new_config: Configs) {
|
||||
let mut merged_matches = new_config.matches;
|
||||
let mut trigger_set = HashSet::new();
|
||||
merged_matches.iter().for_each(|m| {
|
||||
trigger_set.insert(m.trigger.clone());
|
||||
});
|
||||
let parent_matches : Vec<Match> = self.matches.iter().filter(|&m| {
|
||||
!trigger_set.contains(&m.trigger)
|
||||
}).map(|m| m.clone()).collect();
|
||||
|
||||
merged_matches.extend(parent_matches);
|
||||
self.matches = merged_matches;
|
||||
}
|
||||
|
||||
fn merge_default(&mut self, default: &Configs) {
|
||||
let mut trigger_set = HashSet::new();
|
||||
self.matches.iter().for_each(|m| {
|
||||
trigger_set.insert(m.trigger.clone());
|
||||
});
|
||||
let default_matches : Vec<Match> = default.matches.iter().filter(|&m| {
|
||||
!trigger_set.contains(&m.trigger)
|
||||
}).map(|m| m.clone()).collect();
|
||||
|
||||
self.matches.extend(default_matches);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
|
@ -191,78 +222,104 @@ impl ConfigSet {
|
|||
let default_file = dir_path.join(DEFAULT_CONFIG_FILE_NAME);
|
||||
let default = Configs::load_config(default_file.as_path())?;
|
||||
|
||||
// Load user defined configurations
|
||||
// Analyze which config files has to be loaded
|
||||
|
||||
// TODO: loading with parent merging
|
||||
|
||||
let mut specific = Vec::new();
|
||||
let mut target_files = Vec::new();
|
||||
|
||||
let specific_dir = dir_path.join(USER_CONFIGS_FOLDER_NAME);
|
||||
if specific_dir.exists() {
|
||||
// Used to make sure no duplicates are present
|
||||
let mut name_set = HashSet::new(); // TODO: think about integration with packages
|
||||
let dir_entry = WalkDir::new(specific_dir);
|
||||
target_files.extend(dir_entry);
|
||||
}
|
||||
|
||||
let dir_entry = fs::read_dir(specific_dir);
|
||||
if dir_entry.is_err() {
|
||||
return Err(ConfigLoadError::UnableToReadFile)
|
||||
}
|
||||
let dir_entry = dir_entry.unwrap();
|
||||
let package_dir = dir_path.join(PACKAGES_FOLDER_NAME);
|
||||
if package_dir.exists() {
|
||||
let dir_entry = WalkDir::new(package_dir);
|
||||
target_files.extend(dir_entry);
|
||||
}
|
||||
|
||||
for entry in dir_entry {
|
||||
let entry = entry;
|
||||
if let Ok(entry) = entry {
|
||||
let path = entry.path();
|
||||
// Load the user defined config files
|
||||
|
||||
// Skip non-yaml config files
|
||||
if path.extension().unwrap_or_default().to_str().unwrap_or_default() != "yml" {
|
||||
continue;
|
||||
}
|
||||
let mut name_set = HashSet::new();
|
||||
let mut children_map: HashMap<String, Vec<Configs>> = HashMap::new();
|
||||
let mut root_configs = Vec::new();
|
||||
root_configs.push(default);
|
||||
|
||||
let mut config = Configs::load_config(path.as_path())?;
|
||||
for entry in target_files {
|
||||
if let Ok(entry) = entry {
|
||||
let path = entry.path();
|
||||
|
||||
if !config.validate_user_defined_config() {
|
||||
return Err(ConfigLoadError::InvalidParameter(path.to_owned()))
|
||||
}
|
||||
|
||||
if config.name == "default" {
|
||||
return Err(ConfigLoadError::MissingName(path.to_owned()));
|
||||
}
|
||||
|
||||
if name_set.contains(&config.name) {
|
||||
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
|
||||
|
||||
name_set.insert(config.name.clone());
|
||||
specific.push(config);
|
||||
// Skip non-yaml config files
|
||||
if path.extension().unwrap_or_default().to_str().unwrap_or_default() != "yml" {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut config = Configs::load_config(&path)?;
|
||||
|
||||
// Make sure the config does not contain reserved fields
|
||||
if !config.validate_user_defined_config() {
|
||||
return Err(ConfigLoadError::InvalidParameter(path.to_owned()))
|
||||
}
|
||||
|
||||
// No name specified, defaulting to the path name
|
||||
if config.name == "default" {
|
||||
config.name = path.to_str().unwrap_or_default().to_owned();
|
||||
}
|
||||
|
||||
if name_set.contains(&config.name) {
|
||||
return Err(ConfigLoadError::NameDuplicate(path.to_owned()));
|
||||
}
|
||||
|
||||
name_set.insert(config.name.clone());
|
||||
|
||||
if config.parent == "self" { // No parent, root config
|
||||
root_configs.push(config);
|
||||
}else{ // Children config
|
||||
let children_vec = children_map.entry(config.parent.clone()).or_default();
|
||||
children_vec.push(config);
|
||||
}
|
||||
}else{
|
||||
eprintln!("Warning: Unable to read config file: {}", entry.unwrap_err())
|
||||
}
|
||||
}
|
||||
|
||||
// Merge the children config files
|
||||
let mut configs = Vec::new();
|
||||
for root_config in root_configs {
|
||||
let config = ConfigSet::reduce_configs(root_config, &children_map);
|
||||
configs.push(config);
|
||||
}
|
||||
|
||||
// Separate default from specific
|
||||
let default= configs.get(0).unwrap().clone();
|
||||
let mut specific = (&configs[1..]).to_vec().clone();
|
||||
|
||||
// Add default matches to specific configs when needed
|
||||
for config in specific.iter_mut() {
|
||||
if !config.exclude_default_matches {
|
||||
config.merge_default(&default);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ConfigSet {
|
||||
default,
|
||||
specific: specific
|
||||
specific
|
||||
})
|
||||
}
|
||||
|
||||
fn reduce_configs(target: Configs, children_map: &HashMap<String, Vec<Configs>>) -> Configs {
|
||||
if children_map.contains_key(&target.name) {
|
||||
let mut target = target;
|
||||
for children in children_map.get(&target.name).unwrap() {
|
||||
let children = Self::reduce_configs(children.clone(), children_map);
|
||||
target.merge_config(children);
|
||||
}
|
||||
target
|
||||
}else{
|
||||
target
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_default() -> Result<ConfigSet, ConfigLoadError> {
|
||||
let res = dirs::home_dir();
|
||||
if let Some(home_dir) = res {
|
||||
|
@ -550,7 +607,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_set_specific_file_missing_name() {
|
||||
fn test_config_set_specific_file_missing_name_auto_generated() {
|
||||
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, DEFAULT_CONFIG_FILE_CONTENT);
|
||||
|
@ -561,14 +618,18 @@ mod tests {
|
|||
let user_defined_path_copy = user_defined_path.clone();
|
||||
|
||||
let config_set = ConfigSet::load(tmp_dir.path());
|
||||
assert!(config_set.is_err());
|
||||
assert_eq!(config_set.unwrap_err(), ConfigLoadError::MissingName(user_defined_path_copy))
|
||||
assert!(config_set.is_ok());
|
||||
assert_eq!(config_set.unwrap().specific[0].name, user_defined_path_copy.to_str().unwrap_or_default())
|
||||
}
|
||||
|
||||
pub fn create_temp_espanso_directory() -> TempDir {
|
||||
create_temp_espanso_directory_with_default_content(DEFAULT_CONFIG_FILE_CONTENT)
|
||||
}
|
||||
|
||||
pub fn create_temp_espanso_directory_with_default_content(default_content: &str) -> TempDir {
|
||||
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, DEFAULT_CONFIG_FILE_CONTENT);
|
||||
fs::write(default_path, default_content);
|
||||
|
||||
tmp_dir
|
||||
}
|
||||
|
@ -590,6 +651,16 @@ mod tests {
|
|||
create_temp_file_in_dir(&user_config_dir, name, content)
|
||||
}
|
||||
|
||||
pub fn create_package_file(tmp_dir: &Path, package_name: &str, filename: &str, content: &str) -> PathBuf {
|
||||
let package_config_dir = tmp_dir.join(PACKAGES_FOLDER_NAME);
|
||||
let package_dir = package_config_dir.join(package_name);
|
||||
if !package_dir.exists() {
|
||||
create_dir_all(&package_dir);
|
||||
}
|
||||
|
||||
create_temp_file_in_dir(&package_dir, filename, content)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_set_specific_file_duplicate_name() {
|
||||
let tmp_dir = create_temp_espanso_directory();
|
||||
|
@ -679,7 +750,7 @@ mod tests {
|
|||
let user_defined_path2 = create_user_config_file(tmp_dir.path(), "specific2.yml", r###"
|
||||
name: specific1
|
||||
|
||||
exclude_parent_matches: true
|
||||
exclude_default_matches: true
|
||||
|
||||
matches:
|
||||
- trigger: "hello"
|
||||
|
@ -708,7 +779,7 @@ mod tests {
|
|||
let user_defined_path2 = create_user_config_file(tmp_dir.path(), "specific.zzz", r###"
|
||||
name: specific1
|
||||
|
||||
exclude_parent_matches: true
|
||||
exclude_default_matches: true
|
||||
|
||||
matches:
|
||||
- trigger: "hello"
|
||||
|
@ -718,4 +789,196 @@ mod tests {
|
|||
let config_set = ConfigSet::load(tmp_dir.path()).unwrap();
|
||||
assert_eq!(config_set.specific.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_set_no_parent_configs_works_correctly() {
|
||||
let tmp_dir = create_temp_espanso_directory();
|
||||
|
||||
let user_defined_path = create_user_config_file(tmp_dir.path(), "specific.yml", r###"
|
||||
name: specific1
|
||||
"###);
|
||||
|
||||
let user_defined_path2 = create_user_config_file(tmp_dir.path(), "specific2.yml", r###"
|
||||
name: specific2
|
||||
"###);
|
||||
|
||||
let config_set = ConfigSet::load(tmp_dir.path()).unwrap();
|
||||
assert_eq!(config_set.specific.len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_set_default_parent_works_correctly() {
|
||||
let tmp_dir = create_temp_espanso_directory_with_default_content(r###"
|
||||
matches:
|
||||
- trigger: hasta
|
||||
replace: Hasta la vista
|
||||
"###);
|
||||
|
||||
let user_defined_path = create_user_config_file(tmp_dir.path(), "specific.yml", r###"
|
||||
parent: default
|
||||
|
||||
matches:
|
||||
- trigger: "hello"
|
||||
replace: "world"
|
||||
"###);
|
||||
|
||||
let config_set = ConfigSet::load(tmp_dir.path()).unwrap();
|
||||
assert_eq!(config_set.specific.len(), 0);
|
||||
assert_eq!(config_set.default.matches.len(), 2);
|
||||
assert!(config_set.default.matches.iter().any(|m| m.trigger == "hasta"));
|
||||
assert!(config_set.default.matches.iter().any(|m| m.trigger == "hello"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_set_no_parent_should_not_merge() {
|
||||
let tmp_dir = create_temp_espanso_directory_with_default_content(r###"
|
||||
matches:
|
||||
- trigger: hasta
|
||||
replace: Hasta la vista
|
||||
"###);
|
||||
|
||||
let user_defined_path = create_user_config_file(tmp_dir.path(), "specific.yml", r###"
|
||||
matches:
|
||||
- trigger: "hello"
|
||||
replace: "world"
|
||||
"###);
|
||||
|
||||
let config_set = ConfigSet::load(tmp_dir.path()).unwrap();
|
||||
assert_eq!(config_set.specific.len(), 1);
|
||||
assert_eq!(config_set.default.matches.len(), 1);
|
||||
assert!(config_set.default.matches.iter().any(|m| m.trigger == "hasta"));
|
||||
assert!(!config_set.default.matches.iter().any(|m| m.trigger == "hello"));
|
||||
assert!(config_set.specific[0].matches.iter().any(|m| m.trigger == "hello"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_set_default_nested_parent_works_correctly() {
|
||||
let tmp_dir = create_temp_espanso_directory_with_default_content(r###"
|
||||
matches:
|
||||
- trigger: hasta
|
||||
replace: Hasta la vista
|
||||
"###);
|
||||
|
||||
let user_defined_path = create_user_config_file(tmp_dir.path(), "specific.yml", r###"
|
||||
name: custom1
|
||||
parent: default
|
||||
|
||||
matches:
|
||||
- trigger: "hello"
|
||||
replace: "world"
|
||||
"###);
|
||||
|
||||
let user_defined_path2 = create_user_config_file(tmp_dir.path(), "specific2.yml", r###"
|
||||
parent: custom1
|
||||
|
||||
matches:
|
||||
- trigger: "super"
|
||||
replace: "mario"
|
||||
"###);
|
||||
|
||||
let config_set = ConfigSet::load(tmp_dir.path()).unwrap();
|
||||
assert_eq!(config_set.specific.len(), 0);
|
||||
assert_eq!(config_set.default.matches.len(), 3);
|
||||
assert!(config_set.default.matches.iter().any(|m| m.trigger == "hasta"));
|
||||
assert!(config_set.default.matches.iter().any(|m| m.trigger == "hello"));
|
||||
assert!(config_set.default.matches.iter().any(|m| m.trigger == "super"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_set_parent_merge_children_priority_should_be_higher() {
|
||||
let tmp_dir = create_temp_espanso_directory_with_default_content(r###"
|
||||
matches:
|
||||
- trigger: hasta
|
||||
replace: Hasta la vista
|
||||
"###);
|
||||
|
||||
let user_defined_path = create_user_config_file(tmp_dir.path(), "specific.yml", r###"
|
||||
parent: default
|
||||
|
||||
matches:
|
||||
- trigger: "hasta"
|
||||
replace: "world"
|
||||
"###);
|
||||
|
||||
let config_set = ConfigSet::load(tmp_dir.path()).unwrap();
|
||||
assert_eq!(config_set.specific.len(), 0);
|
||||
assert_eq!(config_set.default.matches.len(), 1);
|
||||
assert!(config_set.default.matches.iter().any(|m| m.trigger == "hasta" && m.replace == "world"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_set_package_configs_default_merge() {
|
||||
let tmp_dir = create_temp_espanso_directory_with_default_content(r###"
|
||||
matches:
|
||||
- trigger: hasta
|
||||
replace: Hasta la vista
|
||||
"###);
|
||||
|
||||
let package_path = create_package_file(tmp_dir.path(), "package1", "package.yml", r###"
|
||||
parent: default
|
||||
|
||||
matches:
|
||||
- trigger: "harry"
|
||||
replace: "potter"
|
||||
"###);
|
||||
|
||||
let config_set = ConfigSet::load(tmp_dir.path()).unwrap();
|
||||
assert_eq!(config_set.specific.len(), 0);
|
||||
assert_eq!(config_set.default.matches.len(), 2);
|
||||
assert!(config_set.default.matches.iter().any(|m| m.trigger == "hasta"));
|
||||
assert!(config_set.default.matches.iter().any(|m| m.trigger == "harry"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_set_package_configs_without_merge() {
|
||||
let tmp_dir = create_temp_espanso_directory_with_default_content(r###"
|
||||
matches:
|
||||
- trigger: hasta
|
||||
replace: Hasta la vista
|
||||
"###);
|
||||
|
||||
let package_path = create_package_file(tmp_dir.path(), "package1", "package.yml", r###"
|
||||
matches:
|
||||
- trigger: "harry"
|
||||
replace: "potter"
|
||||
"###);
|
||||
|
||||
let config_set = ConfigSet::load(tmp_dir.path()).unwrap();
|
||||
assert_eq!(config_set.specific.len(), 1);
|
||||
assert_eq!(config_set.default.matches.len(), 1);
|
||||
assert!(config_set.default.matches.iter().any(|m| m.trigger == "hasta"));
|
||||
assert!(config_set.specific[0].matches.iter().any(|m| m.trigger == "harry"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_set_package_configs_multiple_files() {
|
||||
let tmp_dir = create_temp_espanso_directory_with_default_content(r###"
|
||||
matches:
|
||||
- trigger: hasta
|
||||
replace: Hasta la vista
|
||||
"###);
|
||||
|
||||
let package_path = create_package_file(tmp_dir.path(), "package1", "package.yml", r###"
|
||||
name: package1
|
||||
|
||||
matches:
|
||||
- trigger: "harry"
|
||||
replace: "potter"
|
||||
"###);
|
||||
|
||||
let package_path2 = create_package_file(tmp_dir.path(), "package1", "addon.yml", r###"
|
||||
parent: package1
|
||||
|
||||
matches:
|
||||
- trigger: "ron"
|
||||
replace: "weasley"
|
||||
"###);
|
||||
|
||||
let config_set = ConfigSet::load(tmp_dir.path()).unwrap();
|
||||
assert_eq!(config_set.specific.len(), 1);
|
||||
assert_eq!(config_set.default.matches.len(), 1);
|
||||
assert!(config_set.default.matches.iter().any(|m| m.trigger == "hasta"));
|
||||
assert!(config_set.specific[0].matches.iter().any(|m| m.trigger == "harry"));
|
||||
assert!(config_set.specific[0].matches.iter().any(|m| m.trigger == "ron"));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user