diff --git a/espanso-config/src/config/mod.rs b/espanso-config/src/config/mod.rs index 8458eef..47096b6 100644 --- a/espanso-config/src/config/mod.rs +++ b/espanso-config/src/config/mod.rs @@ -24,8 +24,8 @@ use thiserror::Error; mod parse; mod path; mod resolve; -mod store; mod util; +pub(crate) mod store; pub trait Config { fn label(&self) -> &str; diff --git a/espanso-config/src/config/store.rs b/espanso-config/src/config/store.rs index 13c2b02..4a0318c 100644 --- a/espanso-config/src/config/store.rs +++ b/espanso-config/src/config/store.rs @@ -106,6 +106,13 @@ impl DefaultConfigStore { customs, }) } + + pub fn from_configs(default: Box, customs: Vec>) -> Result { + Ok(Self { + default, + customs, + }) + } } #[cfg(test)] diff --git a/espanso-config/src/legacy/config.rs b/espanso-config/src/legacy/config.rs index cc8a6f3..cad182d 100644 --- a/espanso-config/src/legacy/config.rs +++ b/espanso-config/src/legacy/config.rs @@ -153,13 +153,12 @@ fn default_modulo_path() -> Option { fn default_post_inject_delay() -> u64 { 100 } - fn default_wait_for_modifiers_release() -> bool { false } #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Configs { +pub struct LegacyConfig { #[serde(default = "default_name")] pub name: String, @@ -302,7 +301,7 @@ macro_rules! validate_field { }; } -impl Configs { +impl LegacyConfig { /* * Validate the Config instance. * It makes sure that user defined config instances do not define @@ -412,8 +411,8 @@ impl Default for BackendType { } } -impl Configs { - fn load_config(path: &Path) -> Result { +impl LegacyConfig { + fn load_config(path: &Path) -> Result { let file_res = File::open(path); if let Ok(mut file) = file_res { let mut contents = String::new(); @@ -435,7 +434,7 @@ impl Configs { } } - fn merge_overwrite(&mut self, new_config: Configs) { + fn merge_overwrite(&mut self, new_config: LegacyConfig) { // Merge matches let mut merged_matches = new_config.matches; let mut match_trigger_set = HashSet::new(); @@ -473,7 +472,7 @@ impl Configs { self.global_vars = merged_global_vars; } - fn merge_no_overwrite(&mut self, default: &Configs) { + fn merge_no_overwrite(&mut self, default: &LegacyConfig) { // Merge matches let mut match_trigger_set = HashSet::new(); self.matches.iter().for_each(|m| { @@ -527,20 +526,20 @@ fn name_for_global_var(v: &Value) -> String { } #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct ConfigSet { - pub default: Configs, - pub specific: Vec, +pub struct LegacyConfigSet { + pub default: LegacyConfig, + pub specific: Vec, } -impl ConfigSet { - pub fn load(config_dir: &Path, package_dir: &Path) -> Result { +impl LegacyConfigSet { + pub fn load(config_dir: &Path, package_dir: &Path) -> Result { if !config_dir.is_dir() { return Err(ConfigLoadError::InvalidConfigDirectory); } // Load default configuration let default_file = config_dir.join(DEFAULT_CONFIG_FILE_NAME); - let default = Configs::load_config(default_file.as_path())?; + let default = LegacyConfig::load_config(default_file.as_path())?; // Check that a compatible backend is used, otherwise warn the user if cfg!(not(target_os = "linux")) && default.backend == BackendType::Auto { @@ -569,13 +568,13 @@ impl ConfigSet { // Load the user defined config files let mut name_set = HashSet::new(); - let mut children_map: HashMap> = HashMap::new(); - let mut package_map: HashMap> = HashMap::new(); + let mut children_map: HashMap> = HashMap::new(); + let mut package_map: HashMap> = HashMap::new(); let mut root_configs = Vec::new(); root_configs.push(default); let mut file_loader = |entry: walkdir::Result, - dest_map: &mut HashMap>| + dest_map: &mut HashMap>| -> Result<(), ConfigLoadError> { match entry { Ok(entry) => { @@ -598,12 +597,12 @@ impl ConfigSet { .unwrap_or_default() .to_str() .unwrap_or_default() - .starts_with(".") + .starts_with('.') { return Ok(()); } - let mut config = Configs::load_config(&path)?; + let mut config = LegacyConfig::load_config(&path)?; // Make sure the config does not contain reserved fields if !config.validate_user_defined_config() { @@ -651,7 +650,7 @@ impl ConfigSet { // Merge the children config files let mut configs_without_packages = Vec::new(); for root_config in root_configs { - let config = ConfigSet::reduce_configs(root_config, &children_map, true); + let config = LegacyConfigSet::reduce_configs(root_config, &children_map, true); configs_without_packages.push(config); } @@ -660,13 +659,13 @@ impl ConfigSet { // than configs. let mut configs = Vec::new(); for root_config in configs_without_packages { - let config = ConfigSet::reduce_configs(root_config, &package_map, false); + let config = LegacyConfigSet::reduce_configs(root_config, &package_map, false); configs.push(config); } // Separate default from specific let default = configs.get(0).unwrap().clone(); - let mut specific = (&configs[1..]).to_vec().clone(); + let mut specific = (&configs[1..]).to_vec(); // Add default entries to specific configs when needed for config in specific.iter_mut() { @@ -685,14 +684,14 @@ impl ConfigSet { } } - Ok(ConfigSet { default, specific }) + Ok(LegacyConfigSet { default, specific }) } fn reduce_configs( - target: Configs, - children_map: &HashMap>, + target: LegacyConfig, + children_map: &HashMap>, higher_priority: bool, - ) -> Configs { + ) -> LegacyConfig { if children_map.contains_key(&target.name) { let mut target = target; for children in children_map.get(&target.name).unwrap() { @@ -709,7 +708,7 @@ impl ConfigSet { } } - fn has_conflicts(default: &Configs, specific: &Vec) -> bool { + fn has_conflicts(default: &LegacyConfig, specific: &[LegacyConfig]) -> bool { let mut sorted_triggers: Vec = default .matches .iter() @@ -729,7 +728,7 @@ impl ConfigSet { has_conflicts } - fn list_has_conflicts(sorted_list: &Vec) -> bool { + fn list_has_conflicts(sorted_list: &[String]) -> bool { if sorted_list.len() <= 1 { return false; } @@ -817,7 +816,7 @@ mod tests { #[test] fn test_config_file_not_found() { - let config = Configs::load_config(Path::new("invalid/path")); + let config = LegacyConfig::load_config(Path::new("invalid/path")); assert_eq!(config.is_err(), true); assert_eq!(config.unwrap_err(), ConfigLoadError::FileNotFound); } @@ -825,15 +824,14 @@ mod tests { #[test] fn test_config_file_with_bad_yaml_syntax() { let broken_config_file = create_tmp_file(TEST_CONFIG_FILE_WITH_BAD_YAML); - let config = Configs::load_config(broken_config_file.path()); + let config = LegacyConfig::load_config(broken_config_file.path()); match config { - Ok(_) => assert!(false), + Ok(_) => unreachable!(), Err(e) => { match e { ConfigLoadError::InvalidYAML(p, _) => assert_eq!(p, broken_config_file.path().to_owned()), - _ => assert!(false), + _ => unreachable!(), } - assert!(true); } } } @@ -861,7 +859,7 @@ mod tests { "###, ); - let config = Configs::load_config(working_config_file.path()); + let config = LegacyConfig::load_config(working_config_file.path()); assert_eq!(config.unwrap().validate_user_defined_config(), true); } @@ -875,7 +873,7 @@ mod tests { "###, ); - let config = Configs::load_config(working_config_file.path()); + let config = LegacyConfig::load_config(working_config_file.path()); assert_eq!(config.unwrap().validate_user_defined_config(), false); } @@ -889,7 +887,7 @@ mod tests { "###, ); - let config = Configs::load_config(working_config_file.path()); + let config = LegacyConfig::load_config(working_config_file.path()); assert_eq!(config.unwrap().validate_user_defined_config(), false); } @@ -903,7 +901,7 @@ mod tests { "###, ); - let config = Configs::load_config(working_config_file.path()); + let config = LegacyConfig::load_config(working_config_file.path()); assert_eq!(config.unwrap().validate_user_defined_config(), false); } @@ -917,14 +915,14 @@ mod tests { "###, ); - let config = Configs::load_config(working_config_file.path()); + let config = LegacyConfig::load_config(working_config_file.path()); assert_eq!(config.unwrap().validate_user_defined_config(), false); } #[test] fn test_config_loaded_correctly() { let working_config_file = create_tmp_file(TEST_WORKING_CONFIG_FILE); - let config = Configs::load_config(working_config_file.path()); + let config = LegacyConfig::load_config(working_config_file.path()); assert_eq!(config.is_ok(), true); } @@ -981,13 +979,13 @@ mod tests { fn test_config_set_default_content_should_work_correctly() { let (data_dir, package_dir) = create_temp_espanso_directories(); - let config_set = ConfigSet::load(data_dir.path(), package_dir.path()); + let config_set = LegacyConfigSet::load(data_dir.path(), package_dir.path()); assert!(config_set.is_ok()); } #[test] fn test_config_set_load_fail_bad_directory() { - let config_set = ConfigSet::load(Path::new("invalid/path"), Path::new("invalid/path")); + let config_set = LegacyConfigSet::load(Path::new("invalid/path"), Path::new("invalid/path")); assert_eq!(config_set.is_err(), true); assert_eq!( config_set.unwrap_err(), @@ -1000,7 +998,7 @@ mod tests { let data_dir = TempDir::new().expect("unable to create temp directory"); let package_dir = TempDir::new().expect("unable to create package directory"); - let config_set = ConfigSet::load(data_dir.path(), package_dir.path()); + let config_set = LegacyConfigSet::load(data_dir.path(), package_dir.path()); assert_eq!(config_set.is_err(), true); assert_eq!(config_set.unwrap_err(), ConfigLoadError::FileNotFound); } @@ -1011,15 +1009,14 @@ mod tests { create_temp_espanso_directories_with_default_content(TEST_CONFIG_FILE_WITH_BAD_YAML); let default_path = data_dir.path().join(DEFAULT_CONFIG_FILE_NAME); - let config_set = ConfigSet::load(data_dir.path(), package_dir.path()); + let config_set = LegacyConfigSet::load(data_dir.path(), package_dir.path()); match config_set { - Ok(_) => assert!(false), + Ok(_) => unreachable!(), Err(e) => { match e { ConfigLoadError::InvalidYAML(p, _) => assert_eq!(p, default_path), - _ => assert!(false), + _ => unreachable!(), } - assert!(true); } } } @@ -1035,13 +1032,11 @@ mod tests { config_caching_interval: 10000 "###, ); - let user_defined_path_copy = user_defined_path.clone(); - - let config_set = ConfigSet::load(data_dir.path(), package_dir.path()); + let config_set = LegacyConfigSet::load(data_dir.path(), package_dir.path()); assert!(config_set.is_err()); assert_eq!( config_set.unwrap_err(), - ConfigLoadError::InvalidParameter(user_defined_path_copy) + ConfigLoadError::InvalidParameter(user_defined_path) ) } @@ -1056,13 +1051,12 @@ mod tests { backend: Clipboard "###, ); - let user_defined_path_copy = user_defined_path.clone(); - let config_set = ConfigSet::load(data_dir.path(), package_dir.path()); + let config_set = LegacyConfigSet::load(data_dir.path(), package_dir.path()); assert!(config_set.is_ok()); assert_eq!( config_set.unwrap().specific[0].name, - user_defined_path_copy.to_str().unwrap_or_default() + user_defined_path.to_str().unwrap_or_default() ) } @@ -1086,7 +1080,7 @@ mod tests { "###, ); - let config_set = ConfigSet::load(data_dir.path(), package_dir.path()); + let config_set = LegacyConfigSet::load(data_dir.path(), package_dir.path()); assert!(config_set.is_err()); assert!(matches!( &config_set.unwrap_err(), @@ -1118,25 +1112,22 @@ mod tests { "###, ); - let config_set = ConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); + let config_set = LegacyConfigSet::load(data_dir.path(), package_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| triggers_for_match(x)[0] == "hello") - .is_some()); + .any(|x| triggers_for_match(x)[0] == "hello")); assert!(config_set.specific[0] .matches .iter() - .find(|x| triggers_for_match(x)[0] == ":lol") - .is_some()); + .any(|x| triggers_for_match(x)[0] == ":lol")); assert!(config_set.specific[0] .matches .iter() - .find(|x| triggers_for_match(x)[0] == ":yess") - .is_some()); + .any(|x| triggers_for_match(x)[0] == ":yess")); } #[test] @@ -1163,22 +1154,20 @@ mod tests { "###, ); - let config_set = ConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); + let config_set = LegacyConfigSet::load(data_dir.path(), package_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| { + .any(|x| { triggers_for_match(x)[0] == ":lol" && replace_for_match(x) == "newstring" - }) - .is_some()); + })); assert!(config_set.specific[0] .matches .iter() - .find(|x| triggers_for_match(x)[0] == ":yess") - .is_some()); + .any(|x| triggers_for_match(x)[0] == ":yess")); } #[test] @@ -1207,17 +1196,16 @@ mod tests { "###, ); - let config_set = ConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); + let config_set = LegacyConfigSet::load(data_dir.path(), package_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| { + .any(|x| { triggers_for_match(x)[0] == "hello" && replace_for_match(x) == "newstring" - }) - .is_some()); + })); } #[test] @@ -1246,7 +1234,7 @@ mod tests { "###, ); - let config_set = ConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); + let config_set = LegacyConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); assert_eq!(config_set.specific.len(), 0); } @@ -1276,7 +1264,7 @@ mod tests { "###, ); - let config_set = ConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); + let config_set = LegacyConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); assert_eq!(config_set.specific.len(), 0); } @@ -1300,7 +1288,7 @@ mod tests { "###, ); - let config_set = ConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); + let config_set = LegacyConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); assert_eq!(config_set.specific.len(), 2); } @@ -1326,7 +1314,7 @@ mod tests { "###, ); - let config_set = ConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); + let config_set = LegacyConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); assert_eq!(config_set.specific.len(), 0); assert_eq!(config_set.default.matches.len(), 2); assert!(config_set @@ -1361,7 +1349,7 @@ mod tests { "###, ); - let config_set = ConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); + let config_set = LegacyConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); assert_eq!(config_set.specific.len(), 1); assert_eq!(config_set.default.matches.len(), 1); assert!(config_set @@ -1415,7 +1403,7 @@ mod tests { "###, ); - let config_set = ConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); + let config_set = LegacyConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); assert_eq!(config_set.specific.len(), 0); assert_eq!(config_set.default.matches.len(), 3); assert!(config_set @@ -1457,7 +1445,7 @@ mod tests { "###, ); - let config_set = ConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); + let config_set = LegacyConfigSet::load(data_dir.path(), package_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| { @@ -1488,7 +1476,7 @@ mod tests { "###, ); - let config_set = ConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); + let config_set = LegacyConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); assert_eq!(config_set.specific.len(), 0); assert_eq!(config_set.default.matches.len(), 2); assert!(config_set @@ -1526,7 +1514,7 @@ mod tests { "###, ); - let config_set = ConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); + let config_set = LegacyConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); assert_eq!(config_set.specific.len(), 0); assert_eq!(config_set.default.matches.len(), 1); assert_eq!(triggers_for_match(&config_set.default.matches[0])[0], "hasta"); @@ -1554,7 +1542,7 @@ mod tests { "###, ); - let config_set = ConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); + let config_set = LegacyConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); assert_eq!(config_set.specific.len(), 1); assert_eq!(config_set.default.matches.len(), 1); assert!(config_set @@ -1604,7 +1592,7 @@ mod tests { "###, ); - let config_set = ConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); + let config_set = LegacyConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); assert_eq!(config_set.specific.len(), 1); assert_eq!(config_set.default.matches.len(), 1); assert!(config_set @@ -1625,7 +1613,7 @@ mod tests { #[test] fn test_list_has_conflict_no_conflict() { assert_eq!( - ConfigSet::list_has_conflicts(&vec!(":ab".to_owned(), ":bc".to_owned())), + LegacyConfigSet::list_has_conflicts(&[":ab".to_owned(), ":bc".to_owned()]), false ); } @@ -1634,7 +1622,7 @@ mod tests { fn test_list_has_conflict_conflict() { let mut list = vec!["ac".to_owned(), "ab".to_owned(), "abc".to_owned()]; list.sort(); - assert_eq!(ConfigSet::list_has_conflicts(&list), true); + assert_eq!(LegacyConfigSet::list_has_conflicts(&list), true); } #[test] @@ -1661,9 +1649,9 @@ mod tests { "###, ); - let config_set = ConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); + let config_set = LegacyConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); assert_eq!( - ConfigSet::has_conflicts(&config_set.default, &config_set.specific), + LegacyConfigSet::has_conflicts(&config_set.default, &config_set.specific), false ); } @@ -1694,9 +1682,9 @@ mod tests { "###, ); - let config_set = ConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); + let config_set = LegacyConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); assert_eq!( - ConfigSet::has_conflicts(&config_set.default, &config_set.specific), + LegacyConfigSet::has_conflicts(&config_set.default, &config_set.specific), true ); } @@ -1725,9 +1713,9 @@ mod tests { "###, ); - let config_set = ConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); + let config_set = LegacyConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); assert_eq!( - ConfigSet::has_conflicts(&config_set.default, &config_set.specific), + LegacyConfigSet::has_conflicts(&config_set.default, &config_set.specific), true ); } @@ -1767,9 +1755,9 @@ mod tests { "###, ); - let config_set = ConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); + let config_set = LegacyConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); assert_eq!( - ConfigSet::has_conflicts(&config_set.default, &config_set.specific), + LegacyConfigSet::has_conflicts(&config_set.default, &config_set.specific), false ); } @@ -1798,7 +1786,7 @@ mod tests { "###, ); - let config_set = ConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); + let config_set = LegacyConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); assert_eq!(config_set.specific.len(), 1); assert_eq!(config_set.default.global_vars.len(), 1); assert_eq!(config_set.specific[0].global_vars.len(), 2); @@ -1837,7 +1825,7 @@ mod tests { "###, ); - let config_set = ConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); + let config_set = LegacyConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); assert_eq!(config_set.specific.len(), 0); assert_eq!(config_set.default.global_vars.len(), 2); assert!(config_set @@ -1878,7 +1866,7 @@ mod tests { "###, ); - let config_set = ConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); + let config_set = LegacyConfigSet::load(data_dir.path(), package_dir.path()).unwrap(); assert_eq!(config_set.specific.len(), 1); assert_eq!(config_set.default.global_vars.len(), 1); assert_eq!(config_set.specific[0].global_vars.len(), 1); diff --git a/espanso-config/src/legacy/mod.rs b/espanso-config/src/legacy/mod.rs index 31db2f7..9b1b23d 100644 --- a/espanso-config/src/legacy/mod.rs +++ b/espanso-config/src/legacy/mod.rs @@ -1,7 +1,7 @@ /* * This file is part of espanso. * - * Copyright (C) 2019-2021 Federico Terzi + * C title: (), class: (), exec: ()opyright (C) 2019-2021 Federico Terzi * * espanso is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,16 +17,313 @@ * along with espanso. If not, see . */ -use std::path::Path; use anyhow::Result; +use regex::Regex; +use std::{collections::HashMap, path::Path}; -use crate::{config::ConfigStore, matches::store::MatchStore}; +use self::config::LegacyConfig; +use crate::config::store::DefaultConfigStore; +use crate::matches::group::loader::yaml::parse::{YAMLMatch, YAMLVariable}; +use crate::{ + config::Config, + config::{AppProperties, ConfigStore}, + matches::{ + store::{MatchSet, MatchStore}, + Match, Variable, + }, +}; +use std::convert::TryInto; mod config; mod model; -pub fn load(base_dir: &Path) -> Result<(Box, Box)> { - // TODO: load legacy config set and then convert it to the new format +pub fn load( + base_dir: &Path, + package_dir: &Path, +) -> Result<(Box, Box)> { + let config_set = config::LegacyConfigSet::load(base_dir, package_dir)?; - todo!() -} \ No newline at end of file + let (default_config, default_match_group) = split_config(config_set.default); + let mut match_groups = HashMap::new(); + match_groups.insert("default".to_string(), default_match_group); + + let mut custom_configs: Vec> = Vec::new(); + for custom in config_set.specific { + let (custom_config, custom_match_group) = split_config(custom); + match_groups.insert(custom_config.name.clone(), custom_match_group); + custom_configs.push(Box::new(custom_config)); + } + + let config_store = DefaultConfigStore::from_configs(Box::new(default_config), custom_configs)?; + let match_store = LegacyMatchStore::new(match_groups); + + Ok((Box::new(config_store), Box::new(match_store))) +} + +fn split_config(config: LegacyConfig) -> (LegacyInteropConfig, LegacyMatchGroup) { + let global_vars = config + .global_vars + .iter() + .filter_map(|var| { + let var: YAMLVariable = serde_yaml::from_value(var.clone()).ok()?; + let var: Variable = var.try_into().ok()?; + Some(var) + }) + .collect(); + + let matches = config + .matches + .iter() + .filter_map(|var| { + let m: YAMLMatch = serde_yaml::from_value(var.clone()).ok()?; + let m: Match = m.try_into().ok()?; + Some(m) + }) + .collect(); + + let match_group = LegacyMatchGroup { + global_vars, + matches, + }; + + let config: LegacyInteropConfig = config.into(); + (config, match_group) +} + +struct LegacyInteropConfig { + pub name: String, + + match_paths: Vec, + + filter_title: Option, + filter_class: Option, + filter_exec: Option, +} + +impl From for LegacyInteropConfig { + fn from(config: config::LegacyConfig) -> Self { + Self { + name: config.name.clone(), + match_paths: vec![config.name], + filter_title: if !config.filter_title.is_empty() { + Regex::new(&config.filter_title).ok() + } else { + None + }, + filter_class: if !config.filter_class.is_empty() { + Regex::new(&config.filter_class).ok() + } else { + None + }, + filter_exec: if !config.filter_exec.is_empty() { + Regex::new(&config.filter_exec).ok() + } else { + None + }, + } + } +} + +impl Config for LegacyInteropConfig { + fn label(&self) -> &str { + &self.name + } + + fn match_paths(&self) -> &[String] { + &self.match_paths + } + + fn is_match(&self, app: &AppProperties) -> bool { + if self.filter_title.is_none() && self.filter_exec.is_none() && self.filter_class.is_none() { + return false; + } + + let is_title_match = if let Some(title_regex) = self.filter_title.as_ref() { + if let Some(title) = app.title { + title_regex.is_match(title) + } else { + false + } + } else { + true + }; + + let is_exec_match = if let Some(exec_regex) = self.filter_exec.as_ref() { + if let Some(exec) = app.exec { + exec_regex.is_match(exec) + } else { + false + } + } else { + true + }; + + let is_class_match = if let Some(class_regex) = self.filter_class.as_ref() { + if let Some(class) = app.class { + class_regex.is_match(class) + } else { + false + } + } else { + true + }; + + // All the filters that have been specified must be true to define a match + is_exec_match && is_title_match && is_class_match + } +} + +struct LegacyMatchGroup { + matches: Vec, + global_vars: Vec, +} + +struct LegacyMatchStore { + groups: HashMap, +} + +impl LegacyMatchStore { + pub fn new(groups: HashMap) -> Self { + Self { groups } + } +} + +impl MatchStore for LegacyMatchStore { + fn query(&self, paths: &[String]) -> MatchSet { + let group = if !paths.is_empty() { + if let Some(group) = self.groups.get(&paths[0]) { + Some(group) + } else { + None + } + } else { + None + }; + + if let Some(group) = group { + MatchSet { + matches: group.matches.iter().collect(), + global_vars: group.global_vars.iter().collect(), + } + } else { + MatchSet { + matches: Vec::new(), + global_vars: Vec::new(), + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::{fs::create_dir_all, path::Path}; + use tempdir::TempDir; + + pub fn use_test_directory(callback: impl FnOnce(&Path, &Path, &Path)) { + let dir = TempDir::new("tempconfig").unwrap(); + let user_dir = dir.path().join("user"); + create_dir_all(&user_dir).unwrap(); + + let package_dir = TempDir::new("tempconfig").unwrap(); + + callback( + &dunce::canonicalize(&dir.path()).unwrap(), + &dunce::canonicalize(&user_dir).unwrap(), + &dunce::canonicalize(&package_dir.path()).unwrap(), + ); + } + + #[test] + fn load_legacy_works_correctly() { + use_test_directory(|base, user, packages| { + std::fs::write(base.join("default.yml"), r#" + backend: Clipboard + + global_vars: + - name: var1 + type: test + + matches: + - trigger: "hello" + replace: "world" + "#).unwrap(); + + std::fs::write(user.join("specific.yml"), r#" + name: specific + parent: default + + matches: + - trigger: "foo" + replace: "bar" + "#).unwrap(); + + std::fs::write(user.join("separate.yml"), r#" + name: separate + filter_title: "Google" + + matches: + - trigger: "eren" + replace: "mikasa" + "#).unwrap(); + + let (config_store, match_store) = load(base, packages).unwrap(); + + let default_config = config_store.default(); + assert_eq!(default_config.match_paths().len(), 1); + + let active_config = config_store.active(&AppProperties { + title: Some("Google"), + class: None, + exec: None, + }); + assert_eq!(active_config.match_paths().len(), 1); + + let default_fallback = config_store.active(&AppProperties { + title: Some("Yahoo"), + class: None, + exec: None, + }); + assert_eq!(default_fallback.match_paths().len(), 1); + + assert_eq!(match_store.query(default_config.match_paths()).matches.len(), 2); + assert_eq!(match_store.query(default_config.match_paths()).global_vars.len(), 1); + + assert_eq!(match_store.query(active_config.match_paths()).matches.len(), 3); + assert_eq!(match_store.query(active_config.match_paths()).global_vars.len(), 1); + + assert_eq!(match_store.query(default_fallback.match_paths()).matches.len(), 2); + assert_eq!(match_store.query(default_fallback.match_paths()).global_vars.len(), 1); + }); + } + + #[test] + fn load_legacy_with_packages() { + use_test_directory(|base, user, packages| { + std::fs::write(base.join("default.yml"), r#" + backend: Clipboard + + matches: + - trigger: "hello" + replace: "world" + "#).unwrap(); + + create_dir_all(packages.join("test-package")).unwrap(); + std::fs::write(packages.join("test-package").join("package.yml"), r#" + name: test-package + parent: default + + matches: + - trigger: "foo" + replace: "bar" + "#).unwrap(); + + let (config_store, match_store) = load(base, packages).unwrap(); + + let default_config = config_store.default(); + assert_eq!(default_config.match_paths().len(), 1); + + assert_eq!(match_store.query(default_config.match_paths()).matches.len(), 2); + }); + } +} diff --git a/espanso-config/src/matches/group/loader/mod.rs b/espanso-config/src/matches/group/loader/mod.rs index 7e931ee..574269a 100644 --- a/espanso-config/src/matches/group/loader/mod.rs +++ b/espanso-config/src/matches/group/loader/mod.rs @@ -25,7 +25,7 @@ use self::yaml::YAMLImporter; use super::MatchGroup; -mod yaml; +pub(crate) mod yaml; trait Importer { fn is_supported(&self, extension: &str) -> bool; diff --git a/espanso-config/src/matches/group/loader/yaml/mod.rs b/espanso-config/src/matches/group/loader/yaml/mod.rs index 74e699d..5b46238 100644 --- a/espanso-config/src/matches/group/loader/yaml/mod.rs +++ b/espanso-config/src/matches/group/loader/yaml/mod.rs @@ -31,7 +31,7 @@ use crate::matches::{MatchCause, MatchEffect, TextEffect, TriggerCause}; use super::Importer; -mod parse; +pub(crate) mod parse; pub(crate) struct YAMLImporter {} diff --git a/espanso-config/src/matches/group/mod.rs b/espanso-config/src/matches/group/mod.rs index 5943a35..ae7aff0 100644 --- a/espanso-config/src/matches/group/mod.rs +++ b/espanso-config/src/matches/group/mod.rs @@ -22,7 +22,7 @@ use std::path::Path; use super::{Match, Variable}; -mod loader; +pub(crate) mod loader; mod path; #[derive(Debug, Clone, PartialEq)] diff --git a/espanso-config/src/matches/mod.rs b/espanso-config/src/matches/mod.rs index a4b4a42..f5208b8 100644 --- a/espanso-config/src/matches/mod.rs +++ b/espanso-config/src/matches/mod.rs @@ -21,7 +21,7 @@ use serde_yaml::Mapping; use crate::counter::{next_id, StructId}; -mod group; +pub(crate) mod group; pub mod store; #[derive(Debug, Clone)]