From bc19b412ae97861c92725fac7110f39be88be108 Mon Sep 17 00:00:00 2001 From: Federico Terzi Date: Sat, 31 Jul 2021 11:54:53 +0200 Subject: [PATCH] feat(core): first steps in patch system implementation --- espanso/src/cli/worker/config.rs | 19 +-- espanso/src/cli/worker/engine/mod.rs | 2 +- .../engine/process/middleware/render/mod.rs | 6 +- espanso/src/main.rs | 1 + espanso/src/patch/config_store.rs | 79 +++++++++++ espanso/src/patch/mod.rs | 125 ++++++++++++++++++ espanso/src/patch/patches/mod.rs | 96 ++++++++++++++ espanso/src/patch/patches/win/mod.rs | 20 +++ .../patches/win/onenote_for_windows_10.rs | 30 +++++ 9 files changed, 366 insertions(+), 12 deletions(-) create mode 100644 espanso/src/patch/config_store.rs create mode 100644 espanso/src/patch/mod.rs create mode 100644 espanso/src/patch/patches/mod.rs create mode 100644 espanso/src/patch/patches/win/mod.rs create mode 100644 espanso/src/patch/patches/win/onenote_for_windows_10.rs diff --git a/espanso/src/cli/worker/config.rs b/espanso/src/cli/worker/config.rs index 980f1ac..d9625c2 100644 --- a/espanso/src/cli/worker/config.rs +++ b/espanso/src/cli/worker/config.rs @@ -17,7 +17,7 @@ * along with espanso. If not, see . */ -use std::collections::HashSet; +use std::{collections::HashSet, sync::Arc}; use espanso_config::{ config::{AppProperties, Config, ConfigStore}, @@ -45,19 +45,19 @@ impl<'a> ConfigManager<'a> { } } - pub fn active(&self) -> &'a dyn Config { + pub fn active(&self) -> Arc { let current_app = self.app_info_provider.get_info(); let info = to_app_properties(¤t_app); self.config_store.active(&info) } - pub fn active_context(&self) -> (&'a dyn Config, MatchSet) { + pub fn active_context(&self) -> (Arc, MatchSet) { let config = self.active(); let match_paths = config.match_paths(); - (config, self.match_store.query(&match_paths)) + (config.clone(), self.match_store.query(&match_paths)) } - pub fn default(&self) -> &'a dyn Config { + pub fn default(&self) -> Arc{ self.config_store.default() } } @@ -86,16 +86,19 @@ impl<'a> crate::engine::process::MatchFilter for ConfigManager<'a> { } impl<'a> super::engine::process::middleware::render::ConfigProvider<'a> for ConfigManager<'a> { - fn configs(&self) -> Vec<(&'a dyn Config, MatchSet)> { + fn configs(&self) -> Vec<(Arc, MatchSet)> { self .config_store .configs() .into_iter() - .map(|config| (config, self.match_store.query(config.match_paths()))) + .map(|config| { + let match_set = self.match_store.query(config.match_paths()); + (config, match_set) + }) .collect() } - fn active(&self) -> (&'a dyn Config, MatchSet) { + fn active(&self) -> (Arc, MatchSet) { self.active_context() } } diff --git a/espanso/src/cli/worker/engine/mod.rs b/espanso/src/cli/worker/engine/mod.rs index 51b14dc..c9a4305 100644 --- a/espanso/src/cli/worker/engine/mod.rs +++ b/espanso/src/cli/worker/engine/mod.rs @@ -172,7 +172,7 @@ pub fn initialize_and_spawn( let path_provider = PathProviderAdapter::new(&paths); let disable_options = - process::middleware::disable::extract_disable_options(config_manager.default()); + process::middleware::disable::extract_disable_options(&*config_manager.default()); let mut processor = crate::engine::process::default( &matchers, diff --git a/espanso/src/cli/worker/engine/process/middleware/render/mod.rs b/espanso/src/cli/worker/engine/process/middleware/render/mod.rs index 70e1602..64c1510 100644 --- a/espanso/src/cli/worker/engine/process/middleware/render/mod.rs +++ b/espanso/src/cli/worker/engine/process/middleware/render/mod.rs @@ -17,7 +17,7 @@ * along with espanso. If not, see . */ -use std::{cell::RefCell, collections::HashMap}; +use std::{cell::RefCell, collections::HashMap, sync::Arc}; pub mod extension; @@ -37,8 +37,8 @@ pub trait MatchProvider<'a> { } pub trait ConfigProvider<'a> { - fn configs(&self) -> Vec<(&'a dyn Config, MatchSet)>; - fn active(&self) -> (&'a dyn Config, MatchSet); + fn configs(&self) -> Vec<(Arc, MatchSet)>; + fn active(&self) -> (Arc, MatchSet); } pub struct RendererAdapter<'a> { diff --git a/espanso/src/main.rs b/espanso/src/main.rs index 9d9a951..32f2cd2 100644 --- a/espanso/src/main.rs +++ b/espanso/src/main.rs @@ -49,6 +49,7 @@ mod ipc; mod lock; #[macro_use] mod logging; +mod patch; mod path; mod preferences; mod util; diff --git a/espanso/src/patch/config_store.rs b/espanso/src/patch/config_store.rs new file mode 100644 index 0000000..2a8b4fa --- /dev/null +++ b/espanso/src/patch/config_store.rs @@ -0,0 +1,79 @@ +/* + * This file is part of espanso. + * + * Copyright (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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * espanso is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with espanso. If not, see . + */ + +use std::sync::Arc; + +use espanso_config::config::{ConfigStore, Config}; +use log::debug; + +use super::PatchDefinition; + +pub struct PatchedConfigStore { + config_store: Box, + patches: Vec, +} + +impl PatchedConfigStore { + pub fn from_store(config_store: Box) -> Self { + Self::from_store_with_patches(config_store, super::get_builtin_patches()) + } + + pub fn from_store_with_patches(config_store: Box, patches: Vec) -> Self { + // Only keep the patches that should be active in the current system + let active_patches = patches.into_iter().filter(|patch| { + let should_be_activated = (patch.should_be_activated)(); + + if should_be_activated { + debug!("activating '{}' patch", patch.name); + } else { + debug!("skipping '{}' patch", patch.name); + } + + should_be_activated + }).collect(); + + Self { + config_store, + patches: active_patches, + } + } +} + +impl ConfigStore for PatchedConfigStore { + fn default(&self) -> Arc { + self.config_store.default() + } + + fn active<'f>( + &'f self, + app: &espanso_config::config::AppProperties, + ) -> Arc{ + todo!() + } + + fn configs(&self) -> Vec> { + todo!() + } + + fn get_all_match_paths(&self) -> std::collections::HashSet { + self.config_store.get_all_match_paths() + } +} + +// TODO: test \ No newline at end of file diff --git a/espanso/src/patch/mod.rs b/espanso/src/patch/mod.rs new file mode 100644 index 0000000..303e3a3 --- /dev/null +++ b/espanso/src/patch/mod.rs @@ -0,0 +1,125 @@ +/* + * This file is part of espanso. + * + * Copyright (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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * espanso is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with espanso. If not, see . + */ + +use std::sync::Arc; + +use espanso_config::config::{AppProperties, Backend, Config, ToggleKey}; + +mod config_store; +mod patches; + +pub fn get_builtin_patches() -> Vec { + // TODO + vec![ + patches::win::onenote_for_windows_10::patch(), + ] +} + +// TODO: fix visibility levels (pub/pub(crate)) + +// TODO: patches should be "merged" at runtime with the active config, unless a config option (like "apply_patch") +// is set. This is needed because we still want to allow the user to define user-specific configs without +// losing the patch-specific changes + +pub struct PatchDefinition { + pub name: &'static str, + pub should_be_activated: fn() -> bool, + pub patch_config: fn(config: Arc) -> Arc, +} + +pub struct DefaultPatchedConfig { + base: Arc, +} + +pub trait PatchedConfig: Config { + // TODO: can we pass a simple reference here? + fn get_base(&self) -> Arc; + + fn id(&self) -> i32 { + self.get_base().id() + } + + fn label(&self) -> &str { + self.get_base().label() + } + + fn match_paths(&self) -> &[String] { + self.get_base().match_paths() + } + + fn backend(&self) -> Backend { + self.get_base().backend() + } + + fn clipboard_threshold(&self) -> usize { + self.get_base().clipboard_threshold() + } + + fn pre_paste_delay(&self) -> usize { + self.get_base().pre_paste_delay() + } + + fn paste_shortcut_event_delay(&self) -> usize { + self.get_base().paste_shortcut_event_delay() + } + + fn paste_shortcut(&self) -> Option { + self.get_base().paste_shortcut() + } + + fn disable_x11_fast_inject(&self) -> bool { + self.get_base().disable_x11_fast_inject() + } + + fn toggle_key(&self) -> Option { + self.get_base().toggle_key() + } + + fn auto_restart(&self) -> bool { + self.get_base().auto_restart() + } + + fn preserve_clipboard(&self) -> bool { + self.get_base().preserve_clipboard() + } + + fn restore_clipboard_delay(&self) -> usize { + self.get_base().restore_clipboard_delay() + } + + fn inject_delay(&self) -> Option { + self.get_base().inject_delay() + } + + fn key_delay(&self) -> Option { + self.get_base().key_delay() + } + + fn word_separators(&self) -> Vec { + self.get_base().word_separators() + } + + fn backspace_limit(&self) -> usize { + self.get_base().backspace_limit() + } + + fn is_match<'a>(&self, app: &AppProperties<'a>) -> bool { + self.get_base().is_match(app) + } +} diff --git a/espanso/src/patch/patches/mod.rs b/espanso/src/patch/patches/mod.rs new file mode 100644 index 0000000..6303b8f --- /dev/null +++ b/espanso/src/patch/patches/mod.rs @@ -0,0 +1,96 @@ +/* + * This file is part of espanso. + * + * Copyright (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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * espanso is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with espanso. If not, see . + */ + +pub mod win; + + + +// #[macro_export] +// macro_rules! create_patch { +// // TODO: add function body +// ( $should_be_activated:expr, $( $config_field:ident ->$config_type:ty ),* ) => { +// { +// $( +// if $child.$x.is_none() { +// $child.$x = $parent.$x.clone(); +// } +// )* + +// // Build a temporary object to verify that all fields +// // are being used at compile time +// $t { +// $( +// $x: None, +// )* +// }; +// } +// { +// use crate::patch::{ConfigProxy, Patch}; + +// pub struct PatchImpl<'a> { +// patched_config: PatchedConfig<'a>, +// } + +// impl<'a> PatchImpl<'a> { +// pub fn new(default: &'a dyn Config) -> Self { +// Self { +// patched_config: PatchedConfig::new(default), +// } +// } +// } + +// impl<'a> Patch<'a> for PatchImpl<'a> { +// fn should_be_activated(&self) -> bool { +// $should_be_activated +// } + +// fn patch_name(&self) -> &'static str { +// todo!() +// } + +// fn config(&self) -> &'a dyn Config { +// &self.patched_config +// } +// } + +// pub struct PatchedConfig<'a> { +// default: &'a dyn Config, +// } + +// impl<'a> PatchedConfig<'a> { +// pub fn new(default: &'a dyn Config) -> Self { +// Self { default } +// } +// } + +// impl<'a> ConfigProxy<'a> for PatchedConfig<'a> { +// fn get_default(&self) -> &'a dyn espanso_config::config::Config { +// self.default +// } + +// $( +// fn $config_field(&self) -> $config_type { +// todo!() +// } +// )* +// } + +// } +// }; +// } diff --git a/espanso/src/patch/patches/win/mod.rs b/espanso/src/patch/patches/win/mod.rs new file mode 100644 index 0000000..c3cc1a7 --- /dev/null +++ b/espanso/src/patch/patches/win/mod.rs @@ -0,0 +1,20 @@ +/* + * This file is part of espanso. + * + * Copyright (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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * espanso is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with espanso. If not, see . + */ + +pub mod onenote_for_windows_10; \ No newline at end of file diff --git a/espanso/src/patch/patches/win/onenote_for_windows_10.rs b/espanso/src/patch/patches/win/onenote_for_windows_10.rs new file mode 100644 index 0000000..ea8a788 --- /dev/null +++ b/espanso/src/patch/patches/win/onenote_for_windows_10.rs @@ -0,0 +1,30 @@ +/* + * This file is part of espanso. + * + * Copyright (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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * espanso is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with espanso. If not, see . + */ + +use crate::patch::PatchDefinition; + +pub fn patch() -> PatchDefinition { + PatchDefinition { + name: file!(), + should_be_activated: || cfg!(target_os = "windows"), + patch_config: |base| { + todo!() + }, + } +}