feat(config): implement legacy loader

This commit is contained in:
Federico Terzi 2021-03-23 21:57:03 +01:00
parent 1e015eb891
commit 9098d5ac2a
8 changed files with 398 additions and 106 deletions

View File

@ -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;

View File

@ -106,6 +106,13 @@ impl DefaultConfigStore {
customs,
})
}
pub fn from_configs(default: Box<dyn Config>, customs: Vec<Box<dyn Config>>) -> Result<Self> {
Ok(Self {
default,
customs,
})
}
}
#[cfg(test)]

View File

@ -153,13 +153,12 @@ fn default_modulo_path() -> Option<String> {
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<Configs, ConfigLoadError> {
impl LegacyConfig {
fn load_config(path: &Path) -> Result<LegacyConfig, ConfigLoadError> {
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<Configs>,
pub struct LegacyConfigSet {
pub default: LegacyConfig,
pub specific: Vec<LegacyConfig>,
}
impl ConfigSet {
pub fn load(config_dir: &Path, package_dir: &Path) -> Result<ConfigSet, ConfigLoadError> {
impl LegacyConfigSet {
pub fn load(config_dir: &Path, package_dir: &Path) -> Result<LegacyConfigSet, ConfigLoadError> {
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<String, Vec<Configs>> = HashMap::new();
let mut package_map: HashMap<String, Vec<Configs>> = HashMap::new();
let mut children_map: HashMap<String, Vec<LegacyConfig>> = HashMap::new();
let mut package_map: HashMap<String, Vec<LegacyConfig>> = HashMap::new();
let mut root_configs = Vec::new();
root_configs.push(default);
let mut file_loader = |entry: walkdir::Result<DirEntry>,
dest_map: &mut HashMap<String, Vec<Configs>>|
dest_map: &mut HashMap<String, Vec<LegacyConfig>>|
-> 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<String, Vec<Configs>>,
target: LegacyConfig,
children_map: &HashMap<String, Vec<LegacyConfig>>,
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<Configs>) -> bool {
fn has_conflicts(default: &LegacyConfig, specific: &[LegacyConfig]) -> bool {
let mut sorted_triggers: Vec<String> = default
.matches
.iter()
@ -729,7 +728,7 @@ impl ConfigSet {
has_conflicts
}
fn list_has_conflicts(sorted_list: &Vec<String>) -> 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);

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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<dyn ConfigStore>, Box<dyn MatchStore>)> {
// TODO: load legacy config set and then convert it to the new format
pub fn load(
base_dir: &Path,
package_dir: &Path,
) -> Result<(Box<dyn ConfigStore>, Box<dyn MatchStore>)> {
let config_set = config::LegacyConfigSet::load(base_dir, package_dir)?;
todo!()
}
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<Box<dyn Config>> = 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<String>,
filter_title: Option<Regex>,
filter_class: Option<Regex>,
filter_exec: Option<Regex>,
}
impl From<config::LegacyConfig> 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<Match>,
global_vars: Vec<Variable>,
}
struct LegacyMatchStore {
groups: HashMap<String, LegacyMatchGroup>,
}
impl LegacyMatchStore {
pub fn new(groups: HashMap<String, LegacyMatchGroup>) -> 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);
});
}
}

View File

@ -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;

View File

@ -31,7 +31,7 @@ use crate::matches::{MatchCause, MatchEffect, TextEffect, TriggerCause};
use super::Importer;
mod parse;
pub(crate) mod parse;
pub(crate) struct YAMLImporter {}

View File

@ -22,7 +22,7 @@ use std::path::Path;
use super::{Match, Variable};
mod loader;
pub(crate) mod loader;
mod path;
#[derive(Debug, Clone, PartialEq)]

View File

@ -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)]