feat(core): progress on the core pipeline
This commit is contained in:
parent
14fdfe4149
commit
4af4a434a3
12
Cargo.lock
generated
12
Cargo.lock
generated
|
@ -296,6 +296,7 @@ dependencies = [
|
|||
"espanso-inject",
|
||||
"espanso-match",
|
||||
"espanso-path",
|
||||
"espanso-render",
|
||||
"espanso-ui",
|
||||
"lazy_static",
|
||||
"log",
|
||||
|
@ -324,9 +325,11 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"anyhow",
|
||||
"dunce",
|
||||
"enum-as-inner",
|
||||
"glob",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"ordered-float",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_yaml",
|
||||
|
@ -666,6 +669,15 @@ dependencies = [
|
|||
"objc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ordered-float"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "766f840da25490628d8e63e529cd21c014f6600c6b8517add12a6fa6167a6218"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.19"
|
||||
|
|
|
@ -21,6 +21,7 @@ espanso-config = { path = "../espanso-config" }
|
|||
espanso-match = { path = "../espanso-match" }
|
||||
espanso-clipboard = { path = "../espanso-clipboard" }
|
||||
espanso-info = { path = "../espanso-info" }
|
||||
espanso-render = { path = "../espanso-render" }
|
||||
espanso-path = { path = "../espanso-path" }
|
||||
maplit = "1.0.2"
|
||||
simplelog = "0.9.0"
|
||||
|
|
|
@ -17,7 +17,10 @@
|
|||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
collections::{HashMap, HashSet},
|
||||
};
|
||||
|
||||
use crate::engine::process::MatchFilter;
|
||||
use espanso_config::{
|
||||
|
@ -27,6 +30,8 @@ use espanso_config::{
|
|||
use espanso_info::{AppInfo, AppInfoProvider};
|
||||
use std::iter::FromIterator;
|
||||
|
||||
use super::engine::render::ConfigProvider;
|
||||
|
||||
pub struct ConfigManager<'a> {
|
||||
config_store: &'a dyn ConfigStore,
|
||||
match_store: &'a dyn MatchStore,
|
||||
|
@ -46,29 +51,16 @@ impl<'a> ConfigManager<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn active(&self) -> &'a dyn Config {
|
||||
pub fn active(&self) -> &'a dyn Config {
|
||||
let current_app = self.app_info_provider.get_info();
|
||||
let info = to_app_properties(¤t_app);
|
||||
self.config_store.active(&info)
|
||||
}
|
||||
|
||||
fn active_match_set(&self) -> MatchSet {
|
||||
let match_paths = self.active().match_paths();
|
||||
self.match_store.query(&match_paths)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MatchFilter for ConfigManager<'a> {
|
||||
fn filter_active(&self, matches_ids: &[i32]) -> Vec<i32> {
|
||||
let ids_set: HashSet<i32> = HashSet::from_iter(matches_ids.iter().copied());
|
||||
let match_set = self.active_match_set();
|
||||
|
||||
match_set
|
||||
.matches
|
||||
.iter()
|
||||
.filter(|m| ids_set.contains(&m.id))
|
||||
.map(|m| m.id)
|
||||
.collect()
|
||||
pub fn active_context(&self) -> (&'a dyn Config, MatchSet) {
|
||||
let config = self.active();
|
||||
let match_paths = config.match_paths();
|
||||
(config, self.match_store.query(&match_paths))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,3 +72,29 @@ fn to_app_properties(info: &AppInfo) -> AppProperties {
|
|||
exec: info.exec.as_deref(),
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MatchFilter for ConfigManager<'a> {
|
||||
fn filter_active(&self, matches_ids: &[i32]) -> Vec<i32> {
|
||||
let ids_set: HashSet<i32> = HashSet::from_iter(matches_ids.iter().copied());
|
||||
let (_, match_set) = self.active_context();
|
||||
|
||||
match_set
|
||||
.matches
|
||||
.iter()
|
||||
.filter(|m| ids_set.contains(&m.id))
|
||||
.map(|m| m.id)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ConfigProvider<'a> for ConfigManager<'a> {
|
||||
fn configs(&self) -> Vec<(&'a dyn Config, MatchSet)> {
|
||||
self.config_store.configs().into_iter().map(|config| {
|
||||
(config, self.match_store.query(config.match_paths()))
|
||||
}).collect()
|
||||
}
|
||||
|
||||
fn active(&self) -> (&'a dyn Config, MatchSet) {
|
||||
self.active_context()
|
||||
}
|
||||
}
|
||||
|
|
58
espanso/src/cli/worker/engine/match_cache.rs
Normal file
58
espanso/src/cli/worker/engine/match_cache.rs
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
use std::{collections::HashMap, iter::FromIterator};
|
||||
|
||||
use espanso_config::{
|
||||
config::ConfigStore,
|
||||
matches::{store::MatchStore, Match},
|
||||
};
|
||||
|
||||
use super::{multiplex::MatchProvider, render::MatchIterator};
|
||||
|
||||
pub struct MatchCache<'a> {
|
||||
cache: HashMap<i32, &'a Match>,
|
||||
}
|
||||
|
||||
impl<'a> MatchCache<'a> {
|
||||
pub fn load(config_store: &'a dyn ConfigStore, match_store: &'a dyn MatchStore) -> Self {
|
||||
let mut cache = HashMap::new();
|
||||
|
||||
let paths = config_store.get_all_match_paths();
|
||||
let global_set = match_store.query(&Vec::from_iter(paths.into_iter()));
|
||||
|
||||
for m in global_set.matches {
|
||||
cache.insert(m.id, m);
|
||||
}
|
||||
|
||||
Self { cache }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MatchProvider<'a> for MatchCache<'a> {
|
||||
fn get(&self, match_id: i32) -> Option<&'a Match> {
|
||||
self.cache.get(&match_id).map(|m| *m)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MatchIterator<'a> for MatchCache<'a> {
|
||||
fn matches(&self) -> Vec<&'a Match> {
|
||||
self.cache.iter().map(|(_, m)| *m).collect()
|
||||
}
|
||||
}
|
|
@ -81,7 +81,7 @@ impl From<espanso_match::MatchResult<i32>> for MatchResult {
|
|||
Self {
|
||||
id: result.id,
|
||||
trigger: result.trigger,
|
||||
vars: result.vars,
|
||||
args: result.vars,
|
||||
}
|
||||
}
|
||||
}
|
26
espanso/src/cli/worker/engine/mod.rs
Normal file
26
espanso/src/cli/worker/engine/mod.rs
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
pub mod ui;
|
||||
pub mod source;
|
||||
pub mod render;
|
||||
pub mod matcher;
|
||||
pub mod executor;
|
||||
pub mod multiplex;
|
||||
pub mod match_cache;
|
57
espanso/src/cli/worker/engine/multiplex.rs
Normal file
57
espanso/src/cli/worker/engine/multiplex.rs
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use espanso_config::matches::{Match, MatchEffect};
|
||||
|
||||
use crate::engine::{
|
||||
event::{render::RenderingRequestedEvent, Event},
|
||||
process::Multiplexer,
|
||||
};
|
||||
|
||||
pub trait MatchProvider<'a> {
|
||||
fn get(&self, match_id: i32) -> Option<&'a Match>;
|
||||
}
|
||||
|
||||
pub struct MultiplexAdapter<'a> {
|
||||
provider: &'a dyn MatchProvider<'a>,
|
||||
}
|
||||
|
||||
impl<'a> MultiplexAdapter<'a> {
|
||||
pub fn new(provider: &'a dyn MatchProvider<'a>) -> Self {
|
||||
Self { provider }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Multiplexer for MultiplexAdapter<'a> {
|
||||
fn convert(&self, match_id: i32, trigger: String, trigger_args: HashMap<String, String>) -> Option<Event> {
|
||||
let m = self.provider.get(match_id)?;
|
||||
|
||||
match &m.effect {
|
||||
MatchEffect::Text(_) => Some(Event::RenderingRequested(RenderingRequestedEvent {
|
||||
match_id,
|
||||
trigger,
|
||||
trigger_args,
|
||||
})),
|
||||
// TODO: think about rich text and image
|
||||
MatchEffect::None => None,
|
||||
}
|
||||
}
|
||||
}
|
228
espanso/src/cli/worker/engine/render.rs
Normal file
228
espanso/src/cli/worker/engine/render.rs
Normal file
|
@ -0,0 +1,228 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
use std::{cell::RefCell, collections::HashMap, convert::TryInto};
|
||||
|
||||
use espanso_config::{
|
||||
config::Config,
|
||||
matches::{store::MatchSet, Match, MatchCause, MatchEffect},
|
||||
};
|
||||
use espanso_render::{CasingStyle, Context, RenderOptions, Template, Value, Variable};
|
||||
|
||||
use crate::{cli::worker::config::ConfigManager, engine::process::{Renderer, RendererError}};
|
||||
|
||||
pub trait MatchIterator<'a> {
|
||||
fn matches(&self) -> Vec<&'a Match>;
|
||||
}
|
||||
|
||||
pub trait ConfigProvider<'a> {
|
||||
fn configs(&self) -> Vec<(&'a dyn Config, MatchSet)>;
|
||||
fn active(&self) -> (&'a dyn Config, MatchSet);
|
||||
}
|
||||
|
||||
pub struct RendererAdapter<'a> {
|
||||
renderer: &'a dyn espanso_render::Renderer,
|
||||
config_provider: &'a dyn ConfigProvider<'a>,
|
||||
|
||||
template_map: HashMap<i32, Option<Template>>,
|
||||
global_vars_map: HashMap<i32, Variable>,
|
||||
|
||||
context_cache: RefCell<HashMap<i32, Context<'a>>>,
|
||||
}
|
||||
|
||||
impl<'a> RendererAdapter<'a> {
|
||||
pub fn new(
|
||||
match_iterator: &'a dyn MatchIterator,
|
||||
config_provider: &'a dyn ConfigProvider<'a>,
|
||||
renderer: &'a dyn espanso_render::Renderer,
|
||||
) -> Self {
|
||||
let template_map = generate_template_map(match_iterator);
|
||||
let global_vars_map = generate_global_vars_map(config_provider);
|
||||
|
||||
Self {
|
||||
renderer,
|
||||
config_provider,
|
||||
template_map,
|
||||
global_vars_map,
|
||||
context_cache: RefCell::new(HashMap::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: test
|
||||
fn generate_template_map(match_iterator: &dyn MatchIterator) -> HashMap<i32, Option<Template>> {
|
||||
let mut template_map = HashMap::new();
|
||||
for m in match_iterator.matches() {
|
||||
let entry = convert_to_template(m);
|
||||
template_map.insert(m.id, entry);
|
||||
}
|
||||
template_map
|
||||
}
|
||||
|
||||
// TODO: test
|
||||
fn generate_global_vars_map(config_provider: &dyn ConfigProvider) -> HashMap<i32, Variable> {
|
||||
let mut global_vars_map = HashMap::new();
|
||||
|
||||
for (_, match_set) in config_provider.configs() {
|
||||
for var in match_set.global_vars.iter() {
|
||||
if !global_vars_map.contains_key(&var.id) {
|
||||
global_vars_map.insert(var.id, convert_var((*var).clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
global_vars_map
|
||||
}
|
||||
|
||||
// TODO: test
|
||||
fn generate_context<'a>(
|
||||
match_set: &MatchSet,
|
||||
template_map: &'a HashMap<i32, Option<Template>>,
|
||||
global_vars_map: &'a HashMap<i32, Variable>,
|
||||
) -> Context<'a> {
|
||||
let mut templates = Vec::new();
|
||||
let mut global_vars = Vec::new();
|
||||
|
||||
for m in match_set.matches.iter() {
|
||||
if let Some(Some(template)) = template_map.get(&m.id) {
|
||||
templates.push(template);
|
||||
}
|
||||
}
|
||||
|
||||
for var in match_set.global_vars.iter() {
|
||||
if let Some(var) = global_vars_map.get(&var.id) {
|
||||
global_vars.push(var);
|
||||
}
|
||||
}
|
||||
|
||||
Context {
|
||||
templates,
|
||||
global_vars
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: move conversion methods to new file?
|
||||
|
||||
fn convert_to_template(m: &Match) -> Option<Template> {
|
||||
if let MatchEffect::Text(text_effect) = &m.effect {
|
||||
let ids = if let MatchCause::Trigger(cause) = &m.cause {
|
||||
cause.triggers.clone()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
Some(Template {
|
||||
ids,
|
||||
body: text_effect.replace.clone(),
|
||||
vars: convert_vars(text_effect.vars.clone()),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_vars(vars: Vec<espanso_config::matches::Variable>) -> Vec<espanso_render::Variable> {
|
||||
vars.into_iter().map(convert_var).collect()
|
||||
}
|
||||
|
||||
fn convert_var(var: espanso_config::matches::Variable) -> espanso_render::Variable {
|
||||
Variable {
|
||||
name: var.name,
|
||||
var_type: var.var_type,
|
||||
params: convert_params(var.params),
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_params(params: espanso_config::matches::Params) -> espanso_render::Params {
|
||||
let mut new_params = espanso_render::Params::new();
|
||||
for (key, value) in params {
|
||||
new_params.insert(key, convert_value(value));
|
||||
}
|
||||
new_params
|
||||
}
|
||||
|
||||
fn convert_value(value: espanso_config::matches::Value) -> espanso_render::Value {
|
||||
match value {
|
||||
espanso_config::matches::Value::Null => espanso_render::Value::Null,
|
||||
espanso_config::matches::Value::Bool(v) => espanso_render::Value::Bool(v),
|
||||
espanso_config::matches::Value::Number(n) => match n {
|
||||
espanso_config::matches::Number::Integer(i) => {
|
||||
espanso_render::Value::Number(espanso_render::Number::Integer(i))
|
||||
}
|
||||
espanso_config::matches::Number::Float(f) => {
|
||||
espanso_render::Value::Number(espanso_render::Number::Float(f.into_inner()))
|
||||
}
|
||||
},
|
||||
espanso_config::matches::Value::String(s) => espanso_render::Value::String(s),
|
||||
espanso_config::matches::Value::Array(v) => {
|
||||
espanso_render::Value::Array(v.into_iter().map(convert_value).collect())
|
||||
}
|
||||
espanso_config::matches::Value::Object(params) => {
|
||||
espanso_render::Value::Object(convert_params(params))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Renderer<'a> for RendererAdapter<'a> {
|
||||
fn render(&'a self, match_id: i32, trigger_vars: HashMap<String, String>) -> anyhow::Result<String> {
|
||||
if let Some(Some(template)) = self.template_map.get(&match_id) {
|
||||
let (config, match_set) = self.config_provider.active();
|
||||
|
||||
let mut context_cache = self.context_cache.borrow_mut();
|
||||
let context = context_cache.entry(config.id()).or_insert(generate_context(&match_set, &self.template_map, &self.global_vars_map));
|
||||
|
||||
// TODO: calculate the casing style instead of hardcoding it
|
||||
let options = RenderOptions {
|
||||
casing_style: CasingStyle::None,
|
||||
};
|
||||
|
||||
// If some trigger vars are specified, augment the template with them
|
||||
let augmented_template = if !trigger_vars.is_empty() {
|
||||
let mut augmented = template.clone();
|
||||
for (name, value) in trigger_vars {
|
||||
let mut params = espanso_render::Params::new();
|
||||
params.insert("echo".to_string(), Value::String(value));
|
||||
augmented.vars.push(Variable {
|
||||
name,
|
||||
var_type: "echo".to_string(),
|
||||
params,
|
||||
})
|
||||
}
|
||||
Some(augmented)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let template = if let Some(augmented) = augmented_template.as_ref() {
|
||||
augmented
|
||||
} else {
|
||||
template
|
||||
};
|
||||
|
||||
match self.renderer.render(template, context, &options) {
|
||||
espanso_render::RenderResult::Success(body) => Ok(body),
|
||||
espanso_render::RenderResult::Aborted => Err(RendererError::Aborted.into()),
|
||||
espanso_render::RenderResult::Error(err) => Err(RendererError::RenderingError(err).into()),
|
||||
}
|
||||
} else {
|
||||
Err(RendererError::NotFound.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -19,16 +19,13 @@
|
|||
|
||||
use funnel::Source;
|
||||
use process::Matcher;
|
||||
use ui::selector::MatchSelectorAdapter;
|
||||
use engine::ui::selector::MatchSelectorAdapter;
|
||||
|
||||
use crate::engine::{Engine, funnel, process, dispatch};
|
||||
use super::{CliModule, CliModuleArgs};
|
||||
|
||||
mod ui;
|
||||
mod engine;
|
||||
mod config;
|
||||
mod source;
|
||||
mod matcher;
|
||||
mod executor;
|
||||
|
||||
pub fn new() -> CliModule {
|
||||
#[allow(clippy::needless_update)]
|
||||
|
@ -48,21 +45,27 @@ fn worker_main(args: CliModuleArgs) {
|
|||
|
||||
let app_info_provider = espanso_info::get_provider().expect("unable to initialize app info provider");
|
||||
let config_manager = config::ConfigManager::new(&*config_store, &*match_store, &*app_info_provider);
|
||||
let match_converter = matcher::convert::MatchConverter::new(&*config_store, &*match_store);
|
||||
let match_converter = engine::matcher::convert::MatchConverter::new(&*config_store, &*match_store);
|
||||
let match_cache = engine::match_cache::MatchCache::load(&*config_store, &*match_store);
|
||||
|
||||
let detect_source = source::detect::init_and_spawn().expect("failed to initialize detector module");
|
||||
let detect_source = engine::source::detect::init_and_spawn().expect("failed to initialize detector module");
|
||||
let sources: Vec<&dyn Source> = vec![&detect_source];
|
||||
let funnel = funnel::default(&sources);
|
||||
|
||||
let matcher = matcher::rolling::RollingMatcherAdapter::new(&match_converter.get_rolling_matches());
|
||||
let matchers: Vec<&dyn Matcher<matcher::MatcherState>> = vec![&matcher];
|
||||
let matcher = engine::matcher::rolling::RollingMatcherAdapter::new(&match_converter.get_rolling_matches());
|
||||
let matchers: Vec<&dyn Matcher<engine::matcher::MatcherState>> = vec![&matcher];
|
||||
let selector = MatchSelectorAdapter::new();
|
||||
let mut processor = process::default(&matchers, &config_manager, &selector);
|
||||
let multiplexer = engine::multiplex::MultiplexAdapter::new(&match_cache);
|
||||
|
||||
let text_injector = executor::text_injector::TextInjectorAdapter::new();
|
||||
// TODO: add extensions
|
||||
let renderer = espanso_render::create(Vec::new());
|
||||
let renderer_adapter = engine::render::RendererAdapter::new(&match_cache, &config_manager, &renderer);
|
||||
|
||||
let mut processor = process::default(&matchers, &config_manager, &selector, &multiplexer, &renderer_adapter);
|
||||
|
||||
let text_injector = engine::executor::text_injector::TextInjectorAdapter::new();
|
||||
let dispatcher = dispatch::default(&text_injector);
|
||||
|
||||
let mut engine = Engine::new(&funnel, &mut processor, &dispatcher);
|
||||
engine.run();
|
||||
}
|
||||
|
||||
}
|
|
@ -34,7 +34,7 @@ impl<'a> TextInjectExecutor<'a> {
|
|||
impl<'a> Executor for TextInjectExecutor<'a> {
|
||||
fn execute(&self, event: &Event) -> bool {
|
||||
if let Event::TextInject(inject_event) = event {
|
||||
if inject_event.mode == TextInjectMode::Keys {
|
||||
if let Some(TextInjectMode::Keys) = inject_event.force_mode {
|
||||
if let Err(error) = self.injector.inject(&inject_event.text) {
|
||||
error!("text injector reported an error: {:?}", error);
|
||||
}
|
||||
|
|
|
@ -18,10 +18,10 @@
|
|||
*/
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TextInjectEvent {
|
||||
pub struct TextInjectRequest {
|
||||
pub delete_count: i32,
|
||||
pub text: String,
|
||||
pub mode: TextInjectMode,
|
||||
pub force_mode: Option<TextInjectMode>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
|
|
|
@ -28,7 +28,7 @@ pub struct MatchesDetectedEvent {
|
|||
pub struct DetectedMatch {
|
||||
pub id: i32,
|
||||
pub trigger: String,
|
||||
pub vars: HashMap<String, String>,
|
||||
pub args: HashMap<String, String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
|
|
|
@ -20,10 +20,12 @@
|
|||
pub mod keyboard;
|
||||
pub mod inject;
|
||||
pub mod matches;
|
||||
pub mod render;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Event {
|
||||
NOOP,
|
||||
ProcessingError(String),
|
||||
|
||||
// Inputs
|
||||
Keyboard(keyboard::KeyboardEvent),
|
||||
|
@ -32,6 +34,9 @@ pub enum Event {
|
|||
MatchesDetected(matches::MatchesDetectedEvent),
|
||||
MatchSelected(matches::MatchSelectedEvent),
|
||||
|
||||
RenderingRequested(render::RenderingRequestedEvent),
|
||||
Rendered(render::RenderedEvent),
|
||||
|
||||
// Effects
|
||||
TextInject(inject::TextInjectEvent),
|
||||
TextInject(inject::TextInjectRequest),
|
||||
}
|
33
espanso/src/engine/event/render.rs
Normal file
33
espanso/src/engine/event/render.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct RenderingRequestedEvent {
|
||||
pub match_id: i32,
|
||||
pub trigger: String,
|
||||
pub trigger_args: HashMap<String, String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct RenderedEvent {
|
||||
pub trigger: String,
|
||||
pub body: String,
|
||||
}
|
|
@ -19,7 +19,13 @@
|
|||
|
||||
use log::trace;
|
||||
|
||||
use super::{Event, MatchFilter, MatchSelector, Matcher, Middleware, Processor, middleware::match_select::MatchSelectMiddleware, middleware::matcher::MatchMiddleware};
|
||||
use super::{
|
||||
middleware::{
|
||||
match_select::MatchSelectMiddleware, matcher::MatchMiddleware, multiplex::MultiplexMiddleware,
|
||||
render::RenderMiddleware,
|
||||
},
|
||||
Event, MatchFilter, MatchSelector, Matcher, Middleware, Multiplexer, Processor, Renderer,
|
||||
};
|
||||
use std::collections::VecDeque;
|
||||
|
||||
pub struct DefaultProcessor<'a> {
|
||||
|
@ -32,12 +38,16 @@ impl<'a> DefaultProcessor<'a> {
|
|||
matchers: &'a [&'a dyn Matcher<'a, MatcherState>],
|
||||
match_filter: &'a dyn MatchFilter,
|
||||
match_selector: &'a dyn MatchSelector,
|
||||
multiplexer: &'a dyn Multiplexer,
|
||||
renderer: &'a dyn Renderer<'a>,
|
||||
) -> DefaultProcessor<'a> {
|
||||
Self {
|
||||
event_queue: VecDeque::new(),
|
||||
middleware: vec![
|
||||
Box::new(MatchMiddleware::new(matchers)),
|
||||
Box::new(MatchSelectMiddleware::new(match_filter, match_selector)),
|
||||
Box::new(MultiplexMiddleware::new(multiplexer)),
|
||||
Box::new(RenderMiddleware::new(renderer)),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,7 +91,7 @@ impl<'a, State> Middleware for MatchMiddleware<'a, State> {
|
|||
DetectedMatch {
|
||||
id: result.id,
|
||||
trigger: result.trigger,
|
||||
vars: result.vars,
|
||||
args: result.args,
|
||||
}
|
||||
}).collect()
|
||||
})
|
||||
|
|
|
@ -17,5 +17,7 @@
|
|||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
pub mod render;
|
||||
pub mod matcher;
|
||||
pub mod multiplex;
|
||||
pub mod match_select;
|
51
espanso/src/engine/process/middleware/multiplex.rs
Normal file
51
espanso/src/engine/process/middleware/multiplex.rs
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
use log::{debug, error};
|
||||
|
||||
use super::super::Middleware;
|
||||
use crate::engine::{event::{Event, inject::{TextInjectRequest, TextInjectMode}, matches::MatchSelectedEvent}, process::{MatchFilter, MatchSelector, Multiplexer}};
|
||||
|
||||
pub struct MultiplexMiddleware<'a> {
|
||||
multiplexer: &'a dyn Multiplexer,
|
||||
}
|
||||
|
||||
impl<'a> MultiplexMiddleware<'a> {
|
||||
pub fn new(multiplexer: &'a dyn Multiplexer) -> Self {
|
||||
Self { multiplexer }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Middleware for MultiplexMiddleware<'a> {
|
||||
fn next(&self, event: Event, _: &dyn FnMut(Event)) -> Event {
|
||||
if let Event::MatchSelected(m_event) = event {
|
||||
return match self.multiplexer.convert(m_event.chosen.id, m_event.chosen.trigger, m_event.chosen.args) {
|
||||
Some(event) => event,
|
||||
None => {
|
||||
error!("match multiplexing failed");
|
||||
Event::NOOP
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
event
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: test
|
63
espanso/src/engine/process/middleware/render.rs
Normal file
63
espanso/src/engine/process/middleware/render.rs
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
use log::{debug, error};
|
||||
|
||||
use super::super::Middleware;
|
||||
use crate::engine::{event::{Event, inject::{TextInjectRequest, TextInjectMode}, matches::MatchSelectedEvent, render::RenderedEvent}, process::{MatchFilter, MatchSelector, Renderer, RendererError}};
|
||||
|
||||
pub struct RenderMiddleware<'a> {
|
||||
renderer: &'a dyn Renderer<'a>,
|
||||
}
|
||||
|
||||
impl<'a> RenderMiddleware<'a> {
|
||||
pub fn new(renderer: &'a dyn Renderer<'a>) -> Self {
|
||||
Self { renderer }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Middleware for RenderMiddleware<'a> {
|
||||
fn next(&self, event: Event, _: &dyn FnMut(Event)) -> Event {
|
||||
if let Event::RenderingRequested(m_event) = event {
|
||||
match self.renderer.render(m_event.match_id, m_event.trigger_args) {
|
||||
Ok(body) => {
|
||||
return Event::Rendered(RenderedEvent {
|
||||
trigger: m_event.trigger,
|
||||
body,
|
||||
})
|
||||
}
|
||||
Err(err) => {
|
||||
match err.downcast_ref::<RendererError>() {
|
||||
Some(RendererError::Aborted) => {
|
||||
return Event::NOOP
|
||||
}
|
||||
_ => {
|
||||
error!("error during rendering: {}", err);
|
||||
return Event::ProcessingError("An error has occurred during rendering, please examine the logs or contact support.".to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
event
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: test
|
|
@ -17,9 +17,10 @@
|
|||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::{event::keyboard::Key, Event};
|
||||
use anyhow::Result;
|
||||
use std::collections::HashMap;
|
||||
use thiserror::Error;
|
||||
|
||||
mod default;
|
||||
mod middleware;
|
||||
|
@ -52,7 +53,7 @@ pub enum MatcherEvent {
|
|||
pub struct MatchResult {
|
||||
pub id: i32,
|
||||
pub trigger: String,
|
||||
pub vars: HashMap<String, String>,
|
||||
pub args: HashMap<String, String>,
|
||||
}
|
||||
|
||||
pub trait MatchFilter {
|
||||
|
@ -63,10 +64,32 @@ pub trait MatchSelector {
|
|||
fn select(&self, matches_ids: &[i32]) -> Option<i32>;
|
||||
}
|
||||
|
||||
pub trait Multiplexer {
|
||||
fn convert(&self, match_id: i32, trigger: String, trigger_args: HashMap<String, String>) -> Option<Event>;
|
||||
}
|
||||
|
||||
pub trait Renderer<'a> {
|
||||
fn render(&'a self, match_id: i32, trigger_args: HashMap<String, String>) -> Result<String>;
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum RendererError {
|
||||
#[error("rendering error")]
|
||||
RenderingError(#[from] anyhow::Error),
|
||||
|
||||
#[error("match not found")]
|
||||
NotFound,
|
||||
|
||||
#[error("aborted")]
|
||||
Aborted,
|
||||
}
|
||||
|
||||
pub fn default<'a, MatcherState>(
|
||||
matchers: &'a [&'a dyn Matcher<'a, MatcherState>],
|
||||
match_filter: &'a dyn MatchFilter,
|
||||
match_selector: &'a dyn MatchSelector,
|
||||
multiplexer: &'a dyn Multiplexer,
|
||||
renderer: &'a dyn Renderer<'a>,
|
||||
) -> impl Processor + 'a {
|
||||
default::DefaultProcessor::new(matchers, match_filter, match_selector)
|
||||
default::DefaultProcessor::new(matchers, match_filter, match_selector, multiplexer, renderer)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user