From c0de39fdd03664fa3d0b5ef9e6278a43e651ad31 Mon Sep 17 00:00:00 2001 From: Federico Terzi Date: Sat, 3 Apr 2021 13:55:21 +0200 Subject: [PATCH] feat(core): improve basic core structure --- Cargo.lock | 1 + espanso-match/src/lib.rs | 6 +- espanso/Cargo.toml | 3 +- espanso/src/cli/worker/executor/mod.rs | 20 +++ .../src/cli/worker/executor/text_injector.rs | 37 ++++ espanso/src/cli/worker/matcher/mod.rs | 30 ++++ espanso/src/cli/worker/matcher/rolling.rs | 140 +++++++++++++++ espanso/src/cli/worker/mod.rs | 23 ++- espanso/src/cli/worker/source/detect.rs | 163 ++++++++++++++++-- espanso/src/engine/dispatch/default.rs | 10 +- .../engine/dispatch/executor/text_inject.rs | 10 +- espanso/src/engine/dispatch/mod.rs | 2 +- espanso/src/engine/funnel/default.rs | 14 +- espanso/src/engine/funnel/mod.rs | 8 +- espanso/src/engine/mod.rs | 15 +- espanso/src/engine/process/default.rs | 19 +- .../src/engine/process/middleware/matcher.rs | 12 +- espanso/src/engine/process/mod.rs | 13 +- espanso/src/main.rs | 3 +- 19 files changed, 459 insertions(+), 70 deletions(-) create mode 100644 espanso/src/cli/worker/executor/mod.rs create mode 100644 espanso/src/cli/worker/executor/text_injector.rs create mode 100644 espanso/src/cli/worker/matcher/mod.rs create mode 100644 espanso/src/cli/worker/matcher/rolling.rs diff --git a/Cargo.lock b/Cargo.lock index 7133b15..4725fb4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -288,6 +288,7 @@ dependencies = [ "anyhow", "clap", "crossbeam", + "enum-as-inner", "espanso-clipboard", "espanso-config", "espanso-detect", diff --git a/espanso-match/src/lib.rs b/espanso-match/src/lib.rs index 81415c5..735ae6a 100644 --- a/espanso-match/src/lib.rs +++ b/espanso-match/src/lib.rs @@ -28,9 +28,9 @@ mod util; #[derive(Debug, Clone, PartialEq)] pub struct MatchResult { - id: Id, - trigger: String, - vars: HashMap, + pub id: Id, + pub trigger: String, + pub vars: HashMap, } impl Default for MatchResult { diff --git a/espanso/Cargo.toml b/espanso/Cargo.toml index a177579..93513a5 100644 --- a/espanso/Cargo.toml +++ b/espanso/Cargo.toml @@ -29,4 +29,5 @@ anyhow = "1.0.38" thiserror = "1.0.23" clap = "2.33.3" lazy_static = "1.4.0" -crossbeam = "0.8.0" \ No newline at end of file +crossbeam = "0.8.0" +enum-as-inner = "0.3.3" \ No newline at end of file diff --git a/espanso/src/cli/worker/executor/mod.rs b/espanso/src/cli/worker/executor/mod.rs new file mode 100644 index 0000000..93435fd --- /dev/null +++ b/espanso/src/cli/worker/executor/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 text_injector; \ No newline at end of file diff --git a/espanso/src/cli/worker/executor/text_injector.rs b/espanso/src/cli/worker/executor/text_injector.rs new file mode 100644 index 0000000..2a5bc84 --- /dev/null +++ b/espanso/src/cli/worker/executor/text_injector.rs @@ -0,0 +1,37 @@ +/* + * 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::engine::dispatch::TextInjector; + +pub struct TextInjectorAdapter {} + +impl TextInjectorAdapter { + pub fn new() -> Self { + Self {} + } +} + +impl TextInjector for TextInjectorAdapter { + fn inject(&self, text: &str) -> anyhow::Result<()> { + // TODO: implement + println!("INJECT: {}", text); + + Ok(()) + } +} diff --git a/espanso/src/cli/worker/matcher/mod.rs b/espanso/src/cli/worker/matcher/mod.rs new file mode 100644 index 0000000..3e30275 --- /dev/null +++ b/espanso/src/cli/worker/matcher/mod.rs @@ -0,0 +1,30 @@ +use espanso_match::rolling::matcher::RollingMatcherState; + +/* + * 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 enum_as_inner::EnumAsInner; + +pub mod rolling; + +#[derive(Clone, EnumAsInner)] +pub enum MatcherState<'a> { + Rolling(RollingMatcherState<'a, i32>), + // TODO: regex +} \ No newline at end of file diff --git a/espanso/src/cli/worker/matcher/rolling.rs b/espanso/src/cli/worker/matcher/rolling.rs new file mode 100644 index 0000000..b11fe58 --- /dev/null +++ b/espanso/src/cli/worker/matcher/rolling.rs @@ -0,0 +1,140 @@ +/* + * 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 espanso_match::rolling::{matcher::RollingMatcher, RollingMatch}; + +use crate::engine::{ + event::keyboard::Key, + process::{MatchResult, Matcher, MatcherEvent}, +}; + +use super::MatcherState; + +pub struct RollingMatcherAdapter { + matcher: RollingMatcher, +} + +impl RollingMatcherAdapter { + pub fn new() -> Self { + // TODO: pass actual matches + let matches = vec![ + RollingMatch::from_string(1, "esp", &Default::default()), + RollingMatch::from_string(2, "test", &Default::default()), + ]; + + let matcher = RollingMatcher::new(&matches, Default::default()); + + Self { matcher } + } +} + +impl <'a> Matcher<'a, MatcherState<'a>> for RollingMatcherAdapter { + fn process( + &'a self, + prev_state: Option<&MatcherState<'a>>, + event: &MatcherEvent, + ) -> (MatcherState<'a>, Vec) { + use espanso_match::Matcher; + + let prev_state = prev_state.map(|state| { + if let Some(state) = state.as_rolling() { + state + } else { + panic!("invalid state type received in RollingMatcherAdapter") + } + }); + let event = event.into(); + + let (state, results) = self.matcher.process(prev_state, event); + + let enum_state = MatcherState::Rolling(state); + let results: Vec = results.into_iter().map(|result| result.into()).collect(); + + (enum_state, results) + } +} + +impl From<&MatcherEvent> for espanso_match::event::Event { + fn from(event: &MatcherEvent) -> Self { + match event { + MatcherEvent::Key { key, chars } => espanso_match::event::Event::Key { + key: key.clone().into(), + chars: chars.to_owned(), + }, + MatcherEvent::VirtualSeparator => espanso_match::event::Event::VirtualSeparator, + } + } +} + +impl From> for MatchResult { + fn from(result: espanso_match::MatchResult) -> Self { + Self { + id: result.id, + trigger: result.trigger, + vars: result.vars, + } + } +} + +impl From for espanso_match::event::Key { + fn from(key: Key) -> Self { + match key { + Key::Alt => espanso_match::event::Key::Alt, + Key::CapsLock => espanso_match::event::Key::CapsLock, + Key::Control => espanso_match::event::Key::Control, + Key::Meta => espanso_match::event::Key::Meta, + Key::NumLock => espanso_match::event::Key::NumLock, + Key::Shift => espanso_match::event::Key::Shift, + Key::Enter => espanso_match::event::Key::Enter, + Key::Tab => espanso_match::event::Key::Tab, + Key::Space => espanso_match::event::Key::Space, + Key::ArrowDown => espanso_match::event::Key::ArrowDown, + Key::ArrowLeft => espanso_match::event::Key::ArrowLeft, + Key::ArrowRight => espanso_match::event::Key::ArrowRight, + Key::ArrowUp => espanso_match::event::Key::ArrowUp, + Key::End => espanso_match::event::Key::End, + Key::Home => espanso_match::event::Key::Home, + Key::PageDown => espanso_match::event::Key::PageDown, + Key::PageUp => espanso_match::event::Key::PageUp, + Key::Escape => espanso_match::event::Key::Escape, + Key::Backspace => espanso_match::event::Key::Backspace, + Key::F1 => espanso_match::event::Key::F1, + Key::F2 => espanso_match::event::Key::F2, + Key::F3 => espanso_match::event::Key::F3, + Key::F4 => espanso_match::event::Key::F4, + Key::F5 => espanso_match::event::Key::F5, + Key::F6 => espanso_match::event::Key::F6, + Key::F7 => espanso_match::event::Key::F7, + Key::F8 => espanso_match::event::Key::F8, + Key::F9 => espanso_match::event::Key::F9, + Key::F10 => espanso_match::event::Key::F10, + Key::F11 => espanso_match::event::Key::F11, + Key::F12 => espanso_match::event::Key::F12, + Key::F13 => espanso_match::event::Key::F13, + Key::F14 => espanso_match::event::Key::F14, + Key::F15 => espanso_match::event::Key::F15, + Key::F16 => espanso_match::event::Key::F16, + Key::F17 => espanso_match::event::Key::F17, + Key::F18 => espanso_match::event::Key::F18, + Key::F19 => espanso_match::event::Key::F19, + Key::F20 => espanso_match::event::Key::F20, + Key::Other(_) => espanso_match::event::Key::Other, + } + } +} diff --git a/espanso/src/cli/worker/mod.rs b/espanso/src/cli/worker/mod.rs index 74eb4df..037deb2 100644 --- a/espanso/src/cli/worker/mod.rs +++ b/espanso/src/cli/worker/mod.rs @@ -17,10 +17,15 @@ * along with espanso. If not, see . */ -use crate::engine::{Engine, funnel}; +use funnel::Source; +use process::Matcher; + +use crate::engine::{Engine, funnel, process, dispatch}; use super::{CliModule, CliModuleArgs}; mod source; +mod matcher; +mod executor; pub fn new() -> CliModule { #[allow(clippy::needless_update)] @@ -35,10 +40,18 @@ pub fn new() -> CliModule { } fn worker_main(args: CliModuleArgs) { - let funnel = funnel::default(vec![ - Box::new(), - ]); + let detect_source = source::detect::init_and_spawn().unwrap(); // TODO: handle error + let sources: Vec<&dyn Source> = vec![&detect_source]; + let funnel = funnel::default(&sources); - let engine = Engine::new(funnel); + let matcher = matcher::rolling::RollingMatcherAdapter::new(); + let matchers: Vec<&dyn Matcher> = vec![&matcher]; + let mut processor = process::default(&matchers); + + let text_injector = executor::text_injector::TextInjectorAdapter::new(); + let dispatcher = dispatch::default(&text_injector); + + let mut engine = Engine::new(&funnel, &mut processor, &dispatcher); + engine.run(); } diff --git a/espanso/src/cli/worker/source/detect.rs b/espanso/src/cli/worker/source/detect.rs index f9cb687..e544585 100644 --- a/espanso/src/cli/worker/source/detect.rs +++ b/espanso/src/cli/worker/source/detect.rs @@ -17,25 +17,166 @@ * along with espanso. If not, see . */ +use anyhow::Result; use crossbeam::channel::{Receiver, Select, SelectedOperation}; -use espanso_detect::event::InputEvent; +use espanso_detect::{event::InputEvent, Source}; +use log::{error, trace}; -use crate::engine::{event::Event, funnel::Source}; +use crate::engine::{ + event::{ + keyboard::{Key, KeyboardEvent, Status, Variant}, + Event, + }, + funnel, process, +}; +use thiserror::Error; -pub struct DetectorSource { +pub struct DetectSource { receiver: Receiver, } -impl Source for DetectorSource { - fn register(&self, select: &Select) -> i32 { - todo!() +impl<'a> funnel::Source<'a> for DetectSource { + fn register(&'a self, select: &mut Select<'a>) -> usize { + select.recv(&self.receiver) } - fn receive(&self, select: &SelectedOperation) -> Event { - todo!() + fn receive(&self, op: SelectedOperation) -> Event { + let input_event = op + .recv(&self.receiver) + .expect("unable to select data from DetectSource receiver"); + match input_event { + InputEvent::Keyboard(keyboard_event) => Event::Keyboard(KeyboardEvent { + key: keyboard_event.key.into(), + value: keyboard_event.value, + status: keyboard_event.status.into(), + variant: keyboard_event.variant.map(|variant| variant.into()), + }), + InputEvent::Mouse(_) => todo!(), // TODO + InputEvent::HotKey(_) => todo!(), // TODO + } } } -pub fn new() { - todo!() -} \ No newline at end of file +// TODO: pass options +pub fn init_and_spawn() -> Result { + let (sender, receiver) = crossbeam::channel::unbounded(); + let (init_tx, init_rx) = crossbeam::channel::unbounded(); + + if let Err(error) = std::thread::Builder::new() + .name("detect thread".to_string()) + .spawn( + move || match espanso_detect::get_source(Default::default()) { + Ok(mut source) => { + if source.initialize().is_err() { + init_tx + .send(false) + .expect("unable to send to the init_tx channel"); + } else { + init_tx + .send(true) + .expect("unable to send to the init_tx channel"); + + source.eventloop(Box::new(move |event| { + sender + .send(event) + .expect("unable to send to the source channel"); + })).expect("detect eventloop crashed"); + } + } + Err(error) => { + error!("cannot initialize event source: {:?}", error); + init_tx + .send(false) + .expect("unable to send to the init_tx channel"); + } + }, + ) + { + error!("detection thread initialization failed: {:?}", error); + return Err(DetectSourceError::ThreadInitFailed.into()); + } + + // Wait for the initialization status + let has_initialized = init_rx + .recv() + .expect("unable to receive from the init_rx channel"); + if !has_initialized { + return Err(DetectSourceError::InitFailed.into()); + } + + Ok(DetectSource { receiver }) +} + +#[derive(Error, Debug)] +pub enum DetectSourceError { + #[error("detection thread initialization failed")] + ThreadInitFailed, + + #[error("detection source initialization failed")] + InitFailed, +} + +impl From for Key { + fn from(key: espanso_detect::event::Key) -> Self { + match key { + espanso_detect::event::Key::Alt => Key::Alt, + espanso_detect::event::Key::CapsLock => Key::CapsLock, + espanso_detect::event::Key::Control => Key::Control, + espanso_detect::event::Key::Meta => Key::Meta, + espanso_detect::event::Key::NumLock => Key::NumLock, + espanso_detect::event::Key::Shift => Key::Shift, + espanso_detect::event::Key::Enter => Key::Enter, + espanso_detect::event::Key::Tab => Key::Tab, + espanso_detect::event::Key::Space => Key::Space, + espanso_detect::event::Key::ArrowDown => Key::ArrowDown, + espanso_detect::event::Key::ArrowLeft => Key::ArrowLeft, + espanso_detect::event::Key::ArrowRight => Key::ArrowRight, + espanso_detect::event::Key::ArrowUp => Key::ArrowUp, + espanso_detect::event::Key::End => Key::End, + espanso_detect::event::Key::Home => Key::Home, + espanso_detect::event::Key::PageDown => Key::PageDown, + espanso_detect::event::Key::PageUp => Key::PageUp, + espanso_detect::event::Key::Escape => Key::Escape, + espanso_detect::event::Key::Backspace => Key::Backspace, + espanso_detect::event::Key::F1 => Key::F1, + espanso_detect::event::Key::F2 => Key::F2, + espanso_detect::event::Key::F3 => Key::F3, + espanso_detect::event::Key::F4 => Key::F4, + espanso_detect::event::Key::F5 => Key::F5, + espanso_detect::event::Key::F6 => Key::F6, + espanso_detect::event::Key::F7 => Key::F7, + espanso_detect::event::Key::F8 => Key::F8, + espanso_detect::event::Key::F9 => Key::F9, + espanso_detect::event::Key::F10 => Key::F10, + espanso_detect::event::Key::F11 => Key::F11, + espanso_detect::event::Key::F12 => Key::F12, + espanso_detect::event::Key::F13 => Key::F13, + espanso_detect::event::Key::F14 => Key::F14, + espanso_detect::event::Key::F15 => Key::F15, + espanso_detect::event::Key::F16 => Key::F16, + espanso_detect::event::Key::F17 => Key::F17, + espanso_detect::event::Key::F18 => Key::F18, + espanso_detect::event::Key::F19 => Key::F19, + espanso_detect::event::Key::F20 => Key::F20, + espanso_detect::event::Key::Other(code) => Key::Other(code), + } + } +} + +impl From for Variant { + fn from(variant: espanso_detect::event::Variant) -> Self { + match variant { + espanso_detect::event::Variant::Left => Variant::Left, + espanso_detect::event::Variant::Right => Variant::Right, + } + } +} + +impl From for Status { + fn from(status: espanso_detect::event::Status) -> Self { + match status { + espanso_detect::event::Status::Pressed => Status::Pressed, + espanso_detect::event::Status::Released => Status::Released, + } + } +} diff --git a/espanso/src/engine/dispatch/default.rs b/espanso/src/engine/dispatch/default.rs index 9d1b2a8..3c8646b 100644 --- a/espanso/src/engine/dispatch/default.rs +++ b/espanso/src/engine/dispatch/default.rs @@ -20,12 +20,12 @@ use super::{Dispatcher, Executor, TextInjector}; use super::Event; -pub struct DefaultDispatcher { - executors: Vec>, +pub struct DefaultDispatcher<'a> { + executors: Vec>, } -impl DefaultDispatcher { - pub fn new(text_injector: impl TextInjector + 'static) -> Self { +impl <'a> DefaultDispatcher<'a> { + pub fn new(text_injector: &'a dyn TextInjector) -> Self { Self { executors: vec![ Box::new(super::executor::text_inject::TextInjectExecutor::new(text_injector)), @@ -34,7 +34,7 @@ impl DefaultDispatcher { } } -impl Dispatcher for DefaultDispatcher { +impl <'a> Dispatcher for DefaultDispatcher<'a> { fn dispatch(&self, event: Event) { for executor in self.executors.iter() { if executor.execute(&event) { diff --git a/espanso/src/engine/dispatch/executor/text_inject.rs b/espanso/src/engine/dispatch/executor/text_inject.rs index 29f00db..1966324 100644 --- a/espanso/src/engine/dispatch/executor/text_inject.rs +++ b/espanso/src/engine/dispatch/executor/text_inject.rs @@ -21,17 +21,17 @@ use super::super::{Event, Executor, TextInjector}; use crate::engine::event::inject::TextInjectMode; use log::error; -pub struct TextInjectExecutor { - injector: T, +pub struct TextInjectExecutor<'a> { + injector: &'a dyn TextInjector, } -impl TextInjectExecutor { - pub fn new(injector: T) -> Self { +impl<'a> TextInjectExecutor<'a> { + pub fn new(injector: &'a dyn TextInjector) -> Self { Self { injector } } } -impl Executor for TextInjectExecutor { +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 { diff --git a/espanso/src/engine/dispatch/mod.rs b/espanso/src/engine/dispatch/mod.rs index 80709d0..542e054 100644 --- a/espanso/src/engine/dispatch/mod.rs +++ b/espanso/src/engine/dispatch/mod.rs @@ -35,7 +35,7 @@ pub trait TextInjector { fn inject(&self, text: &str) -> Result<()>; } -pub fn default(text_injector: impl TextInjector + 'static) -> impl Dispatcher { +pub fn default<'a>(text_injector: &'a dyn TextInjector) -> impl Dispatcher + 'a { default::DefaultDispatcher::new( text_injector, ) diff --git a/espanso/src/engine/funnel/default.rs b/espanso/src/engine/funnel/default.rs index 63a83c5..cfca9fc 100644 --- a/espanso/src/engine/funnel/default.rs +++ b/espanso/src/engine/funnel/default.rs @@ -21,25 +21,25 @@ use crossbeam::channel::Select; use super::{Funnel, FunnelResult, Source}; -pub struct DefaultFunnel { - sources: Vec>, +pub struct DefaultFunnel<'a> { + sources: &'a [&'a dyn Source<'a>], } -impl DefaultFunnel { - pub fn new(sources: Vec>) -> Self { +impl <'a> DefaultFunnel<'a> { + pub fn new(sources: &'a [&'a dyn Source<'a>]) -> Self { Self { sources } } } -impl Funnel for DefaultFunnel { +impl <'a> Funnel for DefaultFunnel<'a> { fn receive(&self) -> FunnelResult { let mut select = Select::new(); // First register all the sources to the select operation for source in self.sources.iter() { - source.register(&select); + source.register(&mut select); } // Wait for the first source (blocking operation) @@ -50,7 +50,7 @@ impl Funnel for DefaultFunnel { .expect("invalid source index returned by select operation"); // Receive (and convert) the event - let event = source.receive(&op); + let event = source.receive(op); FunnelResult::Event(event) } } diff --git a/espanso/src/engine/funnel/mod.rs b/espanso/src/engine/funnel/mod.rs index f258753..db63349 100644 --- a/espanso/src/engine/funnel/mod.rs +++ b/espanso/src/engine/funnel/mod.rs @@ -25,9 +25,9 @@ use super::Event; mod default; -pub trait Source { - fn register(&self, select: &Select) -> i32; - fn receive(&self, select: &SelectedOperation) -> Event; +pub trait Source<'a> { + fn register<'b>(&'a self, select: &mut Select<'a>) -> usize; + fn receive<'b>(&'a self, op: SelectedOperation) -> Event; } pub trait Funnel { @@ -39,6 +39,6 @@ pub enum FunnelResult { EndOfStream, } -pub fn default(sources: Vec>) -> impl Funnel { +pub fn default<'a>(sources: &'a [&'a dyn Source<'a>]) -> impl Funnel + 'a { DefaultFunnel::new(sources) } \ No newline at end of file diff --git a/espanso/src/engine/mod.rs b/espanso/src/engine/mod.rs index deef302..0350cad 100644 --- a/espanso/src/engine/mod.rs +++ b/espanso/src/engine/mod.rs @@ -18,7 +18,6 @@ */ use log::{debug, trace}; -use std::collections::VecDeque; use self::{ dispatch::Dispatcher, @@ -32,14 +31,14 @@ pub mod event; pub mod process; pub mod funnel; -pub struct Engine { - funnel: TFunnel, - processor: TProcessor, - dispatcher: TDispatcher, +pub struct Engine<'a> { + funnel: &'a dyn Funnel, + processor: &'a mut dyn Processor, + dispatcher: &'a dyn Dispatcher, } -impl Engine { - pub fn new(funnel: TFunnel, processor: TProcessor, dispatcher: TDispatcher) -> Self { +impl <'a> Engine<'a> { + pub fn new(funnel: &'a dyn Funnel, processor: &'a mut dyn Processor, dispatcher: &'a dyn Dispatcher) -> Self { Self { funnel, processor, @@ -51,8 +50,6 @@ impl Engine { - trace!("received event from stream: {:?}", event); - let processed_events = self.processor.process(event); for event in processed_events { self.dispatcher.dispatch(event); diff --git a/espanso/src/engine/process/default.rs b/espanso/src/engine/process/default.rs index 53204e2..2eea714 100644 --- a/espanso/src/engine/process/default.rs +++ b/espanso/src/engine/process/default.rs @@ -17,16 +17,18 @@ * along with espanso. If not, see . */ +use log::trace; + use super::{Event, Matcher, Middleware, Processor, middleware::matcher::MatchMiddleware}; use std::collections::VecDeque; -pub struct DefaultProcessor { +pub struct DefaultProcessor<'a> { event_queue: VecDeque, - middleware: Vec>, + middleware: Vec>, } -impl DefaultProcessor { - pub fn new(matchers: Vec>>) -> Self { +impl <'a> DefaultProcessor<'a> { + pub fn new(matchers: &'a [&'a dyn Matcher<'a, MatcherState>]) -> DefaultProcessor<'a> { Self { event_queue: VecDeque::new(), middleware: vec![ @@ -41,13 +43,16 @@ impl DefaultProcessor { let mut current_queue = VecDeque::new(); let dispatch = |event: Event| { - // TODO: add tracing information + trace!("dispatched event: {:?}", event); current_queue.push_front(event); }; for middleware in self.middleware.iter() { - // TODO: add tracing information + trace!("middleware received event: {:?}", current_event); + current_event = middleware.next(current_event, &dispatch); + + trace!("middleware produced event: {:?}", current_event); } while let Some(event) = current_queue.pop_back() { @@ -61,7 +66,7 @@ impl DefaultProcessor { } } -impl Processor for DefaultProcessor { +impl <'a> Processor for DefaultProcessor<'a> { fn process(&mut self, event: Event) -> Vec { self.event_queue.push_front(event); diff --git a/espanso/src/engine/process/middleware/matcher.rs b/espanso/src/engine/process/middleware/matcher.rs index 7890e93..c208b97 100644 --- a/espanso/src/engine/process/middleware/matcher.rs +++ b/espanso/src/engine/process/middleware/matcher.rs @@ -25,14 +25,14 @@ use crate::engine::{event::{Event, keyboard::{Key, Status}, matches_detected::{M const MAX_HISTORY: usize = 3; // TODO: get as parameter -pub struct MatchMiddleware { - matchers: Vec>>, +pub struct MatchMiddleware<'a, State> { + matchers: &'a [&'a dyn Matcher<'a, State>], matcher_states: RefCell>>, } -impl MatchMiddleware { - pub fn new(matchers: Vec>>) -> Self { +impl<'a, State> MatchMiddleware<'a, State> { + pub fn new(matchers: &'a [&'a dyn Matcher<'a, State>]) -> Self { Self { matchers, matcher_states: RefCell::new(VecDeque::new()), @@ -40,7 +40,7 @@ impl MatchMiddleware { } } -impl Middleware for MatchMiddleware { +impl<'a, State> Middleware for MatchMiddleware<'a, State> { fn next(&self, event: Event, _: &dyn FnMut(Event)) -> Event { let mut matcher_states = self.matcher_states.borrow_mut(); let prev_states = if !matcher_states.is_empty() { @@ -85,6 +85,8 @@ impl Middleware for MatchMiddleware { matcher_states.pop_front(); } + println!("results: {:?}", all_results); + if !all_results.is_empty() { return Event::MatchesDetected(MatchesDetectedEvent { results: all_results.into_iter().map(|result | { diff --git a/espanso/src/engine/process/mod.rs b/espanso/src/engine/process/mod.rs index cee4133..73cb897 100644 --- a/espanso/src/engine/process/mod.rs +++ b/espanso/src/engine/process/mod.rs @@ -35,10 +35,11 @@ pub trait Processor { // Dependency inversion entities -pub trait Matcher { - fn process(&self, prev_state: Option<&State>, event: &MatcherEvent) -> (State, Vec); +pub trait Matcher<'a, State> { + fn process(&'a self, prev_state: Option<&State>, event: &MatcherEvent) -> (State, Vec); } +#[derive(Debug)] pub enum MatcherEvent { Key { key: Key, chars: Option }, VirtualSeparator, @@ -46,11 +47,11 @@ pub enum MatcherEvent { #[derive(Debug, Clone, PartialEq)] pub struct MatchResult { - id: i32, - trigger: String, - vars: HashMap, + pub id: i32, + pub trigger: String, + pub vars: HashMap, } -pub fn default(matchers: Vec>>) -> impl Processor { +pub fn default<'a, MatcherState>(matchers: &'a [&'a dyn Matcher<'a, MatcherState>]) -> impl Processor + 'a { default::DefaultProcessor::new(matchers) } \ No newline at end of file diff --git a/espanso/src/main.rs b/espanso/src/main.rs index d46f122..ce68796 100644 --- a/espanso/src/main.rs +++ b/espanso/src/main.rs @@ -201,7 +201,8 @@ fn main() { let log_level = match matches.occurrences_of("v") { 0 => LevelFilter::Warn, 1 => LevelFilter::Info, - _ => LevelFilter::Debug, + 2 => LevelFilter::Debug, + _ => LevelFilter::Trace, }; let handler = CLI_HANDLERS