feat(core): wire up injectors
This commit is contained in:
parent
4af4a434a3
commit
518f0f8376
84
espanso/src/cli/worker/engine/executor/key_injector.rs
Normal file
84
espanso/src/cli/worker/engine/executor/key_injector.rs
Normal file
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* 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_inject::Injector;
|
||||
|
||||
use crate::engine::dispatch::KeyInjector;
|
||||
|
||||
pub struct KeyInjectorAdapter<'a> {
|
||||
injector: &'a dyn Injector,
|
||||
}
|
||||
|
||||
impl<'a> KeyInjectorAdapter<'a> {
|
||||
pub fn new(injector: &'a dyn Injector) -> Self {
|
||||
Self { injector }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> KeyInjector for KeyInjectorAdapter<'a> {
|
||||
fn inject_sequence(&self, keys: &[crate::engine::event::keyboard::Key]) -> anyhow::Result<()> {
|
||||
let converted_keys: Vec<_> = keys.iter().map(convert_to_inject_key).collect();
|
||||
self.injector.send_keys(&converted_keys, Default::default()) // TODO: handle options
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_to_inject_key(key: &crate::engine::event::keyboard::Key) -> espanso_inject::keys::Key {
|
||||
match key {
|
||||
crate::engine::event::keyboard::Key::Alt => espanso_inject::keys::Key::Alt,
|
||||
crate::engine::event::keyboard::Key::CapsLock => espanso_inject::keys::Key::CapsLock,
|
||||
crate::engine::event::keyboard::Key::Control => espanso_inject::keys::Key::Control,
|
||||
crate::engine::event::keyboard::Key::Meta => espanso_inject::keys::Key::Meta,
|
||||
crate::engine::event::keyboard::Key::NumLock => espanso_inject::keys::Key::NumLock,
|
||||
crate::engine::event::keyboard::Key::Shift => espanso_inject::keys::Key::Shift,
|
||||
crate::engine::event::keyboard::Key::Enter => espanso_inject::keys::Key::Enter,
|
||||
crate::engine::event::keyboard::Key::Tab => espanso_inject::keys::Key::Tab,
|
||||
crate::engine::event::keyboard::Key::Space => espanso_inject::keys::Key::Space,
|
||||
crate::engine::event::keyboard::Key::ArrowDown => espanso_inject::keys::Key::ArrowDown,
|
||||
crate::engine::event::keyboard::Key::ArrowLeft => espanso_inject::keys::Key::ArrowLeft,
|
||||
crate::engine::event::keyboard::Key::ArrowRight => espanso_inject::keys::Key::ArrowRight,
|
||||
crate::engine::event::keyboard::Key::ArrowUp => espanso_inject::keys::Key::ArrowUp,
|
||||
crate::engine::event::keyboard::Key::End => espanso_inject::keys::Key::End,
|
||||
crate::engine::event::keyboard::Key::Home => espanso_inject::keys::Key::Home,
|
||||
crate::engine::event::keyboard::Key::PageDown => espanso_inject::keys::Key::PageDown,
|
||||
crate::engine::event::keyboard::Key::PageUp => espanso_inject::keys::Key::PageUp,
|
||||
crate::engine::event::keyboard::Key::Escape => espanso_inject::keys::Key::Escape,
|
||||
crate::engine::event::keyboard::Key::Backspace => espanso_inject::keys::Key::Backspace,
|
||||
crate::engine::event::keyboard::Key::F1 => espanso_inject::keys::Key::F1,
|
||||
crate::engine::event::keyboard::Key::F2 => espanso_inject::keys::Key::F2,
|
||||
crate::engine::event::keyboard::Key::F3 => espanso_inject::keys::Key::F3,
|
||||
crate::engine::event::keyboard::Key::F4 => espanso_inject::keys::Key::F4,
|
||||
crate::engine::event::keyboard::Key::F5 => espanso_inject::keys::Key::F5,
|
||||
crate::engine::event::keyboard::Key::F6 => espanso_inject::keys::Key::F6,
|
||||
crate::engine::event::keyboard::Key::F7 => espanso_inject::keys::Key::F7,
|
||||
crate::engine::event::keyboard::Key::F8 => espanso_inject::keys::Key::F8,
|
||||
crate::engine::event::keyboard::Key::F9 => espanso_inject::keys::Key::F9,
|
||||
crate::engine::event::keyboard::Key::F10 => espanso_inject::keys::Key::F10,
|
||||
crate::engine::event::keyboard::Key::F11 => espanso_inject::keys::Key::F11,
|
||||
crate::engine::event::keyboard::Key::F12 => espanso_inject::keys::Key::F12,
|
||||
crate::engine::event::keyboard::Key::F13 => espanso_inject::keys::Key::F13,
|
||||
crate::engine::event::keyboard::Key::F14 => espanso_inject::keys::Key::F14,
|
||||
crate::engine::event::keyboard::Key::F15 => espanso_inject::keys::Key::F15,
|
||||
crate::engine::event::keyboard::Key::F16 => espanso_inject::keys::Key::F16,
|
||||
crate::engine::event::keyboard::Key::F17 => espanso_inject::keys::Key::F17,
|
||||
crate::engine::event::keyboard::Key::F18 => espanso_inject::keys::Key::F18,
|
||||
crate::engine::event::keyboard::Key::F19 => espanso_inject::keys::Key::F19,
|
||||
crate::engine::event::keyboard::Key::F20 => espanso_inject::keys::Key::F20,
|
||||
crate::engine::event::keyboard::Key::Other(raw) => espanso_inject::keys::Key::Raw(*raw),
|
||||
}
|
||||
}
|
|
@ -17,4 +17,5 @@
|
|||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
pub mod text_injector;
|
||||
pub mod text_injector;
|
||||
pub mod key_injector;
|
|
@ -17,21 +17,25 @@
|
|||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use espanso_inject::Injector;
|
||||
|
||||
use crate::engine::dispatch::TextInjector;
|
||||
|
||||
pub struct TextInjectorAdapter {}
|
||||
pub struct TextInjectorAdapter<'a> {
|
||||
injector: &'a dyn Injector,
|
||||
}
|
||||
|
||||
impl TextInjectorAdapter {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
impl <'a> TextInjectorAdapter<'a> {
|
||||
pub fn new(injector: &'a dyn Injector) -> Self {
|
||||
Self {
|
||||
injector
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TextInjector for TextInjectorAdapter {
|
||||
fn inject(&self, text: &str) -> anyhow::Result<()> {
|
||||
// TODO: implement
|
||||
println!("INJECT: {}", text);
|
||||
|
||||
Ok(())
|
||||
impl <'a> TextInjector for TextInjectorAdapter<'a> {
|
||||
fn inject_text(&self, text: &str) -> anyhow::Result<()> {
|
||||
// TODO: handle injection options
|
||||
self.injector.send_string(text, Default::default())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,8 +63,11 @@ fn worker_main(args: CliModuleArgs) {
|
|||
|
||||
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 injector = espanso_inject::get_injector(Default::default()).expect("failed to initialize injector module"); // TODO: handle the options
|
||||
|
||||
let text_injector = engine::executor::text_injector::TextInjectorAdapter::new(&*injector);
|
||||
let key_injector = engine::executor::key_injector::KeyInjectorAdapter::new(&*injector);
|
||||
let dispatcher = dispatch::default(&text_injector, &key_injector);
|
||||
|
||||
let mut engine = Engine::new(&funnel, &mut processor, &dispatcher);
|
||||
engine.run();
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use super::{Dispatcher, Executor, TextInjector};
|
||||
use super::{Dispatcher, Executor, KeyInjector, TextInjector};
|
||||
use super::Event;
|
||||
|
||||
pub struct DefaultDispatcher<'a> {
|
||||
|
@ -25,10 +25,11 @@ pub struct DefaultDispatcher<'a> {
|
|||
}
|
||||
|
||||
impl <'a> DefaultDispatcher<'a> {
|
||||
pub fn new(text_injector: &'a dyn TextInjector) -> Self {
|
||||
pub fn new(text_injector: &'a dyn TextInjector, key_injector: &'a dyn KeyInjector) -> Self {
|
||||
Self {
|
||||
executors: vec![
|
||||
Box::new(super::executor::text_inject::TextInjectExecutor::new(text_injector)),
|
||||
Box::new(super::executor::key_inject::KeyInjectExecutor::new(key_injector)),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
44
espanso/src/engine/dispatch/executor/key_inject.rs
Normal file
44
espanso/src/engine/dispatch/executor/key_inject.rs
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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 super::super::{Event, Executor, KeyInjector};
|
||||
use log::error;
|
||||
|
||||
pub struct KeyInjectExecutor<'a> {
|
||||
injector: &'a dyn KeyInjector,
|
||||
}
|
||||
|
||||
impl<'a> KeyInjectExecutor<'a> {
|
||||
pub fn new(injector: &'a dyn KeyInjector) -> Self {
|
||||
Self { injector }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Executor for KeyInjectExecutor<'a> {
|
||||
fn execute(&self, event: &Event) -> bool {
|
||||
if let Event::KeySequenceInject(inject_event) = event {
|
||||
if let Err(error) = self.injector.inject_sequence(&inject_event.keys) {
|
||||
error!("key injector reported an error: {}", error);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
|
@ -17,4 +17,5 @@
|
|||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
pub mod text_inject;
|
||||
pub mod text_inject;
|
||||
pub mod key_inject;
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
use super::super::{Event, Executor, TextInjector};
|
||||
use crate::engine::event::inject::TextInjectMode;
|
||||
use crate::engine::event::text::TextInjectMode;
|
||||
use log::error;
|
||||
|
||||
pub struct TextInjectExecutor<'a> {
|
||||
|
@ -35,7 +35,7 @@ impl<'a> Executor for TextInjectExecutor<'a> {
|
|||
fn execute(&self, event: &Event) -> bool {
|
||||
if let Event::TextInject(inject_event) = event {
|
||||
if let Some(TextInjectMode::Keys) = inject_event.force_mode {
|
||||
if let Err(error) = self.injector.inject(&inject_event.text) {
|
||||
if let Err(error) = self.injector.inject_text(&inject_event.text) {
|
||||
error!("text injector reported an error: {:?}", error);
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
use anyhow::Result;
|
||||
use super::Event;
|
||||
use super::{Event, event::keyboard::Key};
|
||||
|
||||
mod executor;
|
||||
mod default;
|
||||
|
@ -32,11 +32,16 @@ pub trait Dispatcher {
|
|||
}
|
||||
|
||||
pub trait TextInjector {
|
||||
fn inject(&self, text: &str) -> Result<()>;
|
||||
fn inject_text(&self, text: &str) -> Result<()>;
|
||||
}
|
||||
|
||||
pub fn default<'a>(text_injector: &'a dyn TextInjector) -> impl Dispatcher + 'a {
|
||||
pub trait KeyInjector {
|
||||
fn inject_sequence(&self, keys: &[Key]) -> Result<()>;
|
||||
}
|
||||
|
||||
pub fn default<'a>(text_injector: &'a dyn TextInjector, key_injector: &'a dyn KeyInjector) -> impl Dispatcher + 'a {
|
||||
default::DefaultDispatcher::new(
|
||||
text_injector,
|
||||
key_injector,
|
||||
)
|
||||
}
|
|
@ -37,6 +37,11 @@ pub struct KeyboardEvent {
|
|||
pub variant: Option<Variant>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct KeySequenceInjectRequest {
|
||||
pub keys: Vec<Key>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Key {
|
||||
// Modifiers
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
pub mod keyboard;
|
||||
pub mod inject;
|
||||
pub mod text;
|
||||
pub mod matches;
|
||||
pub mod render;
|
||||
|
||||
|
@ -38,5 +38,6 @@ pub enum Event {
|
|||
Rendered(render::RenderedEvent),
|
||||
|
||||
// Effects
|
||||
TextInject(inject::TextInjectRequest),
|
||||
KeySequenceInject(keyboard::KeySequenceInjectRequest),
|
||||
TextInject(text::TextInjectRequest),
|
||||
}
|
|
@ -19,7 +19,6 @@
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct TextInjectRequest {
|
||||
pub delete_count: i32,
|
||||
pub text: String,
|
||||
pub force_mode: Option<TextInjectMode>,
|
||||
}
|
||||
|
@ -28,4 +27,5 @@ pub struct TextInjectRequest {
|
|||
pub enum TextInjectMode {
|
||||
Keys,
|
||||
Clipboard,
|
||||
}
|
||||
}
|
||||
|
|
@ -22,7 +22,7 @@ use log::trace;
|
|||
use super::{
|
||||
middleware::{
|
||||
match_select::MatchSelectMiddleware, matcher::MatchMiddleware, multiplex::MultiplexMiddleware,
|
||||
render::RenderMiddleware,
|
||||
render::RenderMiddleware, action::ActionMiddleware,
|
||||
},
|
||||
Event, MatchFilter, MatchSelector, Matcher, Middleware, Multiplexer, Processor, Renderer,
|
||||
};
|
||||
|
@ -48,6 +48,7 @@ impl<'a> DefaultProcessor<'a> {
|
|||
Box::new(MatchSelectMiddleware::new(match_filter, match_selector)),
|
||||
Box::new(MultiplexMiddleware::new(multiplexer)),
|
||||
Box::new(RenderMiddleware::new(renderer)),
|
||||
Box::new(ActionMiddleware::new()),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
@ -57,7 +58,7 @@ impl<'a> DefaultProcessor<'a> {
|
|||
let mut current_event = event;
|
||||
|
||||
let mut current_queue = VecDeque::new();
|
||||
let dispatch = |event: Event| {
|
||||
let mut dispatch = |event: Event| {
|
||||
trace!("dispatched event: {:?}", event);
|
||||
current_queue.push_front(event);
|
||||
};
|
||||
|
@ -65,7 +66,7 @@ impl<'a> DefaultProcessor<'a> {
|
|||
for middleware in self.middleware.iter() {
|
||||
trace!("middleware received event: {:?}", current_event);
|
||||
|
||||
current_event = middleware.next(current_event, &dispatch);
|
||||
current_event = middleware.next(current_event, &mut dispatch);
|
||||
|
||||
trace!("middleware produced event: {:?}", current_event);
|
||||
}
|
||||
|
|
57
espanso/src/engine/process/middleware/action.rs
Normal file
57
espanso/src/engine/process/middleware/action.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 log::{debug, error};
|
||||
|
||||
use super::super::Middleware;
|
||||
use crate::engine::{event::{Event, keyboard::{Key, KeySequenceInjectRequest}, matches::MatchSelectedEvent, text::{TextInjectMode, TextInjectRequest}}, process::{MatchFilter, MatchSelector, Multiplexer}};
|
||||
|
||||
pub struct ActionMiddleware {
|
||||
}
|
||||
|
||||
impl ActionMiddleware {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl Middleware for ActionMiddleware {
|
||||
fn next(&self, event: Event, dispatch: &mut dyn FnMut(Event)) -> Event {
|
||||
if let Event::Rendered(m_event) = &event {
|
||||
let delete_count = m_event.trigger.len();
|
||||
let delete_sequence: Vec<_> = (0..delete_count).map(|_| Key::Backspace).collect();
|
||||
|
||||
dispatch(Event::TextInject(TextInjectRequest {
|
||||
text: m_event.body.clone(),
|
||||
force_mode: Some(TextInjectMode::Keys), // TODO: determine this one dynamically
|
||||
}));
|
||||
|
||||
// This is executed before the dispatched event
|
||||
return Event::KeySequenceInject(KeySequenceInjectRequest {
|
||||
keys: delete_sequence
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: handle images
|
||||
|
||||
event
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: test
|
|
@ -43,7 +43,7 @@ impl<'a> MatchSelectMiddleware<'a> {
|
|||
}
|
||||
|
||||
impl<'a> Middleware for MatchSelectMiddleware<'a> {
|
||||
fn next(&self, event: Event, _: &dyn FnMut(Event)) -> Event {
|
||||
fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event {
|
||||
if let Event::MatchesDetected(m_event) = event {
|
||||
let matches_ids: Vec<i32> = m_event.matches.iter().map(|m| m.id).collect();
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ impl<'a, State> MatchMiddleware<'a, State> {
|
|||
}
|
||||
|
||||
impl<'a, State> Middleware for MatchMiddleware<'a, State> {
|
||||
fn next(&self, event: Event, _: &dyn FnMut(Event)) -> Event {
|
||||
fn next(&self, event: Event, _: &mut dyn FnMut(Event)) -> Event {
|
||||
let mut matcher_states = self.matcher_states.borrow_mut();
|
||||
let prev_states = if !matcher_states.is_empty() {
|
||||
matcher_states.get(matcher_states.len() - 1)
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
pub mod action;
|
||||
pub mod render;
|
||||
pub mod matcher;
|
||||
pub mod multiplex;
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
use log::{debug, error};
|
||||
|
||||
use super::super::Middleware;
|
||||
use crate::engine::{event::{Event, inject::{TextInjectRequest, TextInjectMode}, matches::MatchSelectedEvent}, process::{MatchFilter, MatchSelector, Multiplexer}};
|
||||
use crate::engine::{event::{Event, text::{TextInjectRequest, TextInjectMode}, matches::MatchSelectedEvent}, process::{MatchFilter, MatchSelector, Multiplexer}};
|
||||
|
||||
pub struct MultiplexMiddleware<'a> {
|
||||
multiplexer: &'a dyn Multiplexer,
|
||||
|
@ -33,7 +33,7 @@ impl<'a> MultiplexMiddleware<'a> {
|
|||
}
|
||||
|
||||
impl<'a> Middleware for MultiplexMiddleware<'a> {
|
||||
fn next(&self, event: Event, _: &dyn FnMut(Event)) -> Event {
|
||||
fn next(&self, event: Event, _: &mut 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,
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
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}};
|
||||
use crate::engine::{event::{Event, text::{TextInjectRequest, TextInjectMode}, matches::MatchSelectedEvent, render::RenderedEvent}, process::{MatchFilter, MatchSelector, Renderer, RendererError}};
|
||||
|
||||
pub struct RenderMiddleware<'a> {
|
||||
renderer: &'a dyn Renderer<'a>,
|
||||
|
@ -33,7 +33,7 @@ impl<'a> RenderMiddleware<'a> {
|
|||
}
|
||||
|
||||
impl<'a> Middleware for RenderMiddleware<'a> {
|
||||
fn next(&self, event: Event, _: &dyn FnMut(Event)) -> Event {
|
||||
fn next(&self, event: Event, _: &mut 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) => {
|
||||
|
|
|
@ -26,7 +26,7 @@ mod default;
|
|||
mod middleware;
|
||||
|
||||
pub trait Middleware {
|
||||
fn next(&self, event: Event, dispatch: &dyn FnMut(Event)) -> Event;
|
||||
fn next(&self, event: Event, dispatch: &mut dyn FnMut(Event)) -> Event;
|
||||
}
|
||||
|
||||
pub trait Processor {
|
||||
|
|
Loading…
Reference in New Issue
Block a user