feat(core): improve basic core structure
This commit is contained in:
parent
a4958ff352
commit
c0de39fdd0
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -288,6 +288,7 @@ dependencies = [
|
|||
"anyhow",
|
||||
"clap",
|
||||
"crossbeam",
|
||||
"enum-as-inner",
|
||||
"espanso-clipboard",
|
||||
"espanso-config",
|
||||
"espanso-detect",
|
||||
|
|
|
@ -28,9 +28,9 @@ mod util;
|
|||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct MatchResult<Id> {
|
||||
id: Id,
|
||||
trigger: String,
|
||||
vars: HashMap<String, String>,
|
||||
pub id: Id,
|
||||
pub trigger: String,
|
||||
pub vars: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl<Id: Default> Default for MatchResult<Id> {
|
||||
|
|
|
@ -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"
|
||||
crossbeam = "0.8.0"
|
||||
enum-as-inner = "0.3.3"
|
20
espanso/src/cli/worker/executor/mod.rs
Normal file
20
espanso/src/cli/worker/executor/mod.rs
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
pub mod text_injector;
|
37
espanso/src/cli/worker/executor/text_injector.rs
Normal file
37
espanso/src/cli/worker/executor/text_injector.rs
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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(())
|
||||
}
|
||||
}
|
30
espanso/src/cli/worker/matcher/mod.rs
Normal file
30
espanso/src/cli/worker/matcher/mod.rs
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use enum_as_inner::EnumAsInner;
|
||||
|
||||
pub mod rolling;
|
||||
|
||||
#[derive(Clone, EnumAsInner)]
|
||||
pub enum MatcherState<'a> {
|
||||
Rolling(RollingMatcherState<'a, i32>),
|
||||
// TODO: regex
|
||||
}
|
140
espanso/src/cli/worker/matcher/rolling.rs
Normal file
140
espanso/src/cli/worker/matcher/rolling.rs
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<i32>,
|
||||
}
|
||||
|
||||
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<MatchResult>) {
|
||||
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<MatchResult> = 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<espanso_match::MatchResult<i32>> for MatchResult {
|
||||
fn from(result: espanso_match::MatchResult<i32>) -> Self {
|
||||
Self {
|
||||
id: result.id,
|
||||
trigger: result.trigger,
|
||||
vars: result.vars,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Key> 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,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,10 +17,15 @@
|
|||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<matcher::MatcherState>> = 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();
|
||||
}
|
||||
|
||||
|
|
|
@ -17,25 +17,166 @@
|
|||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<InputEvent>,
|
||||
}
|
||||
|
||||
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!()
|
||||
}
|
||||
// TODO: pass options
|
||||
pub fn init_and_spawn() -> Result<DetectSource> {
|
||||
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<espanso_detect::event::Key> 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<espanso_detect::event::Variant> 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<espanso_detect::event::Status> 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,12 +20,12 @@
|
|||
use super::{Dispatcher, Executor, TextInjector};
|
||||
use super::Event;
|
||||
|
||||
pub struct DefaultDispatcher {
|
||||
executors: Vec<Box<dyn Executor>>,
|
||||
pub struct DefaultDispatcher<'a> {
|
||||
executors: Vec<Box<dyn Executor + 'a>>,
|
||||
}
|
||||
|
||||
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) {
|
||||
|
|
|
@ -21,17 +21,17 @@ use super::super::{Event, Executor, TextInjector};
|
|||
use crate::engine::event::inject::TextInjectMode;
|
||||
use log::error;
|
||||
|
||||
pub struct TextInjectExecutor<T: TextInjector> {
|
||||
injector: T,
|
||||
pub struct TextInjectExecutor<'a> {
|
||||
injector: &'a dyn TextInjector,
|
||||
}
|
||||
|
||||
impl<T: TextInjector> TextInjectExecutor<T> {
|
||||
pub fn new(injector: T) -> Self {
|
||||
impl<'a> TextInjectExecutor<'a> {
|
||||
pub fn new(injector: &'a dyn TextInjector) -> Self {
|
||||
Self { injector }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TextInjector> Executor for TextInjectExecutor<T> {
|
||||
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 {
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
|
|
@ -21,25 +21,25 @@ use crossbeam::channel::Select;
|
|||
|
||||
use super::{Funnel, FunnelResult, Source};
|
||||
|
||||
pub struct DefaultFunnel {
|
||||
sources: Vec<Box<dyn Source>>,
|
||||
pub struct DefaultFunnel<'a> {
|
||||
sources: &'a [&'a dyn Source<'a>],
|
||||
}
|
||||
|
||||
impl DefaultFunnel {
|
||||
pub fn new(sources: Vec<Box<dyn Source>>) -> 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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Box<dyn Source>>) -> impl Funnel {
|
||||
pub fn default<'a>(sources: &'a [&'a dyn Source<'a>]) -> impl Funnel + 'a {
|
||||
DefaultFunnel::new(sources)
|
||||
}
|
|
@ -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<TFunnel: Funnel, TProcessor: Processor, TDispatcher: Dispatcher> {
|
||||
funnel: TFunnel,
|
||||
processor: TProcessor,
|
||||
dispatcher: TDispatcher,
|
||||
pub struct Engine<'a> {
|
||||
funnel: &'a dyn Funnel,
|
||||
processor: &'a mut dyn Processor,
|
||||
dispatcher: &'a dyn Dispatcher,
|
||||
}
|
||||
|
||||
impl <TFunnel: Funnel, TProcessor: Processor, TDispatcher: Dispatcher> Engine<TFunnel, TProcessor, TDispatcher> {
|
||||
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 <TFunnel: Funnel, TProcessor: Processor, TDispatcher: Dispatcher> Engine<TF
|
|||
loop {
|
||||
match self.funnel.receive() {
|
||||
FunnelResult::Event(event) => {
|
||||
trace!("received event from stream: {:?}", event);
|
||||
|
||||
let processed_events = self.processor.process(event);
|
||||
for event in processed_events {
|
||||
self.dispatcher.dispatch(event);
|
||||
|
|
|
@ -17,16 +17,18 @@
|
|||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<Event>,
|
||||
middleware: Vec<Box<dyn Middleware>>,
|
||||
middleware: Vec<Box<dyn Middleware + 'a>>,
|
||||
}
|
||||
|
||||
impl DefaultProcessor {
|
||||
pub fn new<MatcherState: 'static>(matchers: Vec<Box<dyn Matcher<MatcherState>>>) -> Self {
|
||||
impl <'a> DefaultProcessor<'a> {
|
||||
pub fn new<MatcherState>(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<Event> {
|
||||
self.event_queue.push_front(event);
|
||||
|
||||
|
|
|
@ -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<State> {
|
||||
matchers: Vec<Box<dyn Matcher<State>>>,
|
||||
pub struct MatchMiddleware<'a, State> {
|
||||
matchers: &'a [&'a dyn Matcher<'a, State>],
|
||||
|
||||
matcher_states: RefCell<VecDeque<Vec<State>>>,
|
||||
}
|
||||
|
||||
impl<State> MatchMiddleware<State> {
|
||||
pub fn new(matchers: Vec<Box<dyn Matcher<State>>>) -> 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<State> MatchMiddleware<State> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<State> Middleware for MatchMiddleware<State> {
|
||||
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<State> Middleware for MatchMiddleware<State> {
|
|||
matcher_states.pop_front();
|
||||
}
|
||||
|
||||
println!("results: {:?}", all_results);
|
||||
|
||||
if !all_results.is_empty() {
|
||||
return Event::MatchesDetected(MatchesDetectedEvent {
|
||||
results: all_results.into_iter().map(|result | {
|
||||
|
|
|
@ -35,10 +35,11 @@ pub trait Processor {
|
|||
|
||||
// Dependency inversion entities
|
||||
|
||||
pub trait Matcher<State> {
|
||||
fn process(&self, prev_state: Option<&State>, event: &MatcherEvent) -> (State, Vec<MatchResult>);
|
||||
pub trait Matcher<'a, State> {
|
||||
fn process(&'a self, prev_state: Option<&State>, event: &MatcherEvent) -> (State, Vec<MatchResult>);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum MatcherEvent {
|
||||
Key { key: Key, chars: Option<String> },
|
||||
VirtualSeparator,
|
||||
|
@ -46,11 +47,11 @@ pub enum MatcherEvent {
|
|||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct MatchResult {
|
||||
id: i32,
|
||||
trigger: String,
|
||||
vars: HashMap<String, String>,
|
||||
pub id: i32,
|
||||
pub trigger: String,
|
||||
pub vars: HashMap<String, String>,
|
||||
}
|
||||
|
||||
pub fn default<MatcherState: 'static>(matchers: Vec<Box<dyn Matcher<MatcherState>>>) -> impl Processor {
|
||||
pub fn default<'a, MatcherState>(matchers: &'a [&'a dyn Matcher<'a, MatcherState>]) -> impl Processor + 'a {
|
||||
default::DefaultProcessor::new(matchers)
|
||||
}
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue
Block a user