feat(core): complete work on patch system structure
This commit is contained in:
parent
bc19b412ae
commit
0112603ff7
|
@ -18,7 +18,11 @@
|
|||
*/
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use espanso_config::{config::ConfigStore, error::{ErrorLevel, NonFatalErrorSet}, matches::store::MatchStore};
|
||||
use espanso_config::{
|
||||
config::ConfigStore,
|
||||
error::{ErrorLevel, NonFatalErrorSet},
|
||||
matches::store::MatchStore,
|
||||
};
|
||||
use log::{error, info, warn};
|
||||
use std::path::Path;
|
||||
|
||||
|
@ -80,7 +84,8 @@ pub fn load_config(config_path: &Path, packages_path: &Path) -> Result<ConfigLoa
|
|||
.context("unable to load legacy config")?;
|
||||
|
||||
Ok(ConfigLoadResult {
|
||||
config_store,
|
||||
// Apply the built-in patches
|
||||
config_store: crate::patch::patch_store(config_store),
|
||||
match_store,
|
||||
is_legacy_config: true,
|
||||
non_fatal_errors: Vec::new(),
|
||||
|
@ -93,7 +98,10 @@ pub fn load_config(config_path: &Path, packages_path: &Path) -> Result<ConfigLoa
|
|||
if !non_fatal_errors.is_empty() {
|
||||
warn!("------- detected some errors in the configuration: -------");
|
||||
for non_fatal_error_set in &non_fatal_errors {
|
||||
warn!(">>> {}", non_fatal_error_set.file.to_string_lossy().to_string());
|
||||
warn!(
|
||||
">>> {}",
|
||||
non_fatal_error_set.file.to_string_lossy().to_string()
|
||||
);
|
||||
for record in &non_fatal_error_set.errors {
|
||||
if record.level == ErrorLevel::Error {
|
||||
error!("{:?}", record.error);
|
||||
|
@ -106,7 +114,8 @@ pub fn load_config(config_path: &Path, packages_path: &Path) -> Result<ConfigLoa
|
|||
}
|
||||
|
||||
Ok(ConfigLoadResult {
|
||||
config_store,
|
||||
// Apply the built-in patches
|
||||
config_store: crate::patch::patch_store(config_store),
|
||||
match_store,
|
||||
is_legacy_config: false,
|
||||
non_fatal_errors,
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
use std::sync::Arc;
|
||||
|
||||
use espanso_config::config::{ConfigStore, Config};
|
||||
use espanso_config::config::{Config, ConfigStore};
|
||||
use log::debug;
|
||||
|
||||
use super::PatchDefinition;
|
||||
|
@ -34,20 +34,26 @@ impl PatchedConfigStore {
|
|||
Self::from_store_with_patches(config_store, super::get_builtin_patches())
|
||||
}
|
||||
|
||||
pub fn from_store_with_patches(config_store: Box<dyn ConfigStore>, patches: Vec<PatchDefinition>) -> Self {
|
||||
pub fn from_store_with_patches(
|
||||
config_store: Box<dyn ConfigStore>,
|
||||
patches: Vec<PatchDefinition>,
|
||||
) -> 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)();
|
||||
let active_patches = patches
|
||||
.into_iter()
|
||||
.filter(|patch| {
|
||||
let is_enabled = (patch.is_enabled)();
|
||||
|
||||
if should_be_activated {
|
||||
debug!("activating '{}' patch", patch.name);
|
||||
} else {
|
||||
debug!("skipping '{}' patch", patch.name);
|
||||
}
|
||||
if is_enabled {
|
||||
debug!("enabled '{}' patch", patch.name);
|
||||
} else {
|
||||
debug!("skipping '{}' patch", patch.name);
|
||||
}
|
||||
|
||||
is_enabled
|
||||
})
|
||||
.collect();
|
||||
|
||||
should_be_activated
|
||||
}).collect();
|
||||
|
||||
Self {
|
||||
config_store,
|
||||
patches: active_patches,
|
||||
|
@ -60,15 +66,19 @@ impl ConfigStore for PatchedConfigStore {
|
|||
self.config_store.default()
|
||||
}
|
||||
|
||||
fn active<'f>(
|
||||
&'f self,
|
||||
app: &espanso_config::config::AppProperties,
|
||||
) -> Arc<dyn Config>{
|
||||
todo!()
|
||||
fn active<'f>(&'f self, app: &espanso_config::config::AppProperties) -> Arc<dyn Config> {
|
||||
let active_config = self.config_store.active(app);
|
||||
|
||||
// Check if a patch should be applied
|
||||
if let Some(patch) = self.patches.iter().find(|patch| (patch.should_patch)(app)) {
|
||||
(patch.apply)(active_config)
|
||||
} else {
|
||||
active_config
|
||||
}
|
||||
}
|
||||
|
||||
fn configs(&self) -> Vec<Arc<dyn Config>> {
|
||||
todo!()
|
||||
self.config_store.configs()
|
||||
}
|
||||
|
||||
fn get_all_match_paths(&self) -> std::collections::HashSet<String> {
|
||||
|
@ -76,4 +86,4 @@ impl ConfigStore for PatchedConfigStore {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: test
|
||||
// TODO: test
|
||||
|
|
|
@ -19,107 +19,34 @@
|
|||
|
||||
use std::sync::Arc;
|
||||
|
||||
use espanso_config::config::{AppProperties, Backend, Config, ToggleKey};
|
||||
use espanso_config::config::{AppProperties, Config, ConfigStore};
|
||||
|
||||
mod config_store;
|
||||
mod patches;
|
||||
|
||||
pub fn get_builtin_patches() -> Vec<PatchDefinition> {
|
||||
// TODO
|
||||
vec![
|
||||
patches::win::onenote_for_windows_10::patch(),
|
||||
]
|
||||
pub fn patch_store(store: Box<dyn ConfigStore>) -> Box<dyn ConfigStore> {
|
||||
Box::new(config_store::PatchedConfigStore::from_store(store))
|
||||
}
|
||||
|
||||
// TODO: fix visibility levels (pub/pub(crate))
|
||||
fn get_builtin_patches() -> Vec<PatchDefinition> {
|
||||
#[cfg(target_os = "windows")]
|
||||
return vec![
|
||||
patches::win::onenote_for_windows_10::patch(),
|
||||
];
|
||||
|
||||
// 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
|
||||
#[cfg(target_os = "macos")]
|
||||
return vec![];
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
return vec![
|
||||
// TODO: all the terminals registered in the legacy version + libre office
|
||||
// + firefox
|
||||
];
|
||||
}
|
||||
|
||||
pub struct PatchDefinition {
|
||||
pub name: &'static str,
|
||||
pub should_be_activated: fn() -> bool,
|
||||
pub patch_config: fn(config: Arc<dyn Config>) -> Arc<dyn PatchedConfig>,
|
||||
}
|
||||
|
||||
pub struct DefaultPatchedConfig {
|
||||
base: Arc<dyn Config>,
|
||||
}
|
||||
|
||||
pub trait PatchedConfig: Config {
|
||||
// TODO: can we pass a simple reference here?
|
||||
fn get_base(&self) -> Arc<dyn Config>;
|
||||
|
||||
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<String> {
|
||||
self.get_base().paste_shortcut()
|
||||
}
|
||||
|
||||
fn disable_x11_fast_inject(&self) -> bool {
|
||||
self.get_base().disable_x11_fast_inject()
|
||||
}
|
||||
|
||||
fn toggle_key(&self) -> Option<ToggleKey> {
|
||||
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<usize> {
|
||||
self.get_base().inject_delay()
|
||||
}
|
||||
|
||||
fn key_delay(&self) -> Option<usize> {
|
||||
self.get_base().key_delay()
|
||||
}
|
||||
|
||||
fn word_separators(&self) -> Vec<String> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
pub is_enabled: fn() -> bool,
|
||||
pub should_patch: fn(app: &AppProperties) -> bool,
|
||||
pub apply: fn(config: Arc<dyn Config>) -> Arc<dyn Config>,
|
||||
}
|
76
espanso/src/patch/patches/macros.rs
Normal file
76
espanso/src/patch/patches/macros.rs
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! generate_patchable_config {
|
||||
( $struct_name:ident, $( $config_field:ident ->$config_type:ty ),* ) => {
|
||||
use std::sync::Arc;
|
||||
use espanso_config::config::{AppProperties, Config};
|
||||
|
||||
pub struct $struct_name {
|
||||
base: Arc<dyn Config>,
|
||||
patch: Patches,
|
||||
}
|
||||
|
||||
impl $struct_name {
|
||||
#[allow(dead_code)]
|
||||
pub fn patch(base: Arc<dyn Config>, patch: Patches) -> Self {
|
||||
Self {
|
||||
base,
|
||||
patch,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Patches {
|
||||
$(
|
||||
pub $config_field: Option<$config_type>,
|
||||
)*
|
||||
}
|
||||
|
||||
impl Config for $struct_name {
|
||||
$(
|
||||
fn $config_field(&self) -> $config_type {
|
||||
if let Some(patched) = self.patch.$config_field.clone() {
|
||||
return patched;
|
||||
}
|
||||
|
||||
self.base.$config_field()
|
||||
}
|
||||
)*
|
||||
|
||||
fn id(&self) -> i32 {
|
||||
self.base.id()
|
||||
}
|
||||
|
||||
fn label(&self) -> &str {
|
||||
self.base.label()
|
||||
}
|
||||
|
||||
fn match_paths(&self) -> &[String] {
|
||||
self.base.match_paths()
|
||||
}
|
||||
|
||||
fn is_match<'b>(&self, app: &AppProperties<'b>) -> bool {
|
||||
self.base.is_match(app)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -16,81 +16,29 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use espanso_config::config::{Backend, ToggleKey};
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub mod win;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
|
||||
// #[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!()
|
||||
// }
|
||||
// )*
|
||||
// }
|
||||
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
generate_patchable_config!(
|
||||
PatchedConfig,
|
||||
backend -> Backend,
|
||||
clipboard_threshold -> usize,
|
||||
pre_paste_delay -> usize,
|
||||
paste_shortcut_event_delay -> usize,
|
||||
paste_shortcut -> Option<String>,
|
||||
disable_x11_fast_inject -> bool,
|
||||
toggle_key -> Option<ToggleKey>,
|
||||
auto_restart -> bool,
|
||||
preserve_clipboard -> bool,
|
||||
restore_clipboard_delay -> usize,
|
||||
inject_delay -> Option<usize>,
|
||||
key_delay -> Option<usize>,
|
||||
word_separators -> Vec<String>,
|
||||
backspace_limit -> usize
|
||||
);
|
||||
|
|
|
@ -17,14 +17,24 @@
|
|||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::patch::patches::{PatchedConfig, Patches};
|
||||
use crate::patch::PatchDefinition;
|
||||
|
||||
pub fn patch() -> PatchDefinition {
|
||||
PatchDefinition {
|
||||
name: file!(),
|
||||
should_be_activated: || cfg!(target_os = "windows"),
|
||||
patch_config: |base| {
|
||||
todo!()
|
||||
name: module_path!().split(":").last().unwrap_or("unknown"),
|
||||
is_enabled: || cfg!(target_os = "windows"),
|
||||
should_patch: |app| app.title.unwrap_or_default().contains("OneNote"),
|
||||
apply: |base| {
|
||||
Arc::new(PatchedConfig::patch(
|
||||
base,
|
||||
Patches {
|
||||
key_delay: Some(Some(10)),
|
||||
..Default::default()
|
||||
},
|
||||
))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user