wip: implementing app command dispatch

This commit is contained in:
🪞👃🪞 2025-01-14 19:03:08 +01:00
parent d393cab2d8
commit 12faadef44
31 changed files with 598 additions and 551 deletions

View file

@ -10,7 +10,9 @@ use crate::*;
}
pub trait Command<S>: Send + Sync + Sized {
fn execute (self, state: &mut S) -> Perhaps<Self>;
fn delegate <T> (self, state: &mut S, wrap: impl Fn(Self)->T) -> Perhaps<T> {
fn delegate <T> (self, state: &mut S, wrap: impl Fn(Self)->T) -> Perhaps<T>
where Self: Sized
{
Ok(self.execute(state)?.map(wrap))
}
}

View file

@ -1,7 +1,8 @@
use crate::*;
/// Turns an EDN item sequence into a command enum variant.
pub trait EdnCommand<C>: Command<C> {
fn from_edn <'a> (state: &C, head: &EdnItem<&str>, tail: &'a [EdnItem<String>]) -> Self;
fn from_edn <'a> (state: &C, head: &EdnItem<&str>, tail: &'a [EdnItem<&'a str>])
-> Option<Self>;
}
/** Implement `EdnCommand` for given `State` and `Command` */
#[macro_export] macro_rules! edn_command {
@ -27,7 +28,11 @@ pub trait EdnCommand<C>: Command<C> {
$command:expr
))* }) => {
impl EdnCommand<$State> for $Command {
fn from_edn <'a> ($state: &$State, head: &EdnItem<&str>, tail: &'a [EdnItem<String>]) -> Self {
fn from_edn <'a> (
$state: &$State,
head: &EdnItem<&str>,
tail: &'a [EdnItem<&'a str>]
) -> Option<Self> {
$(if let (EdnItem::Key($key), [ // if the identifier matches
// bind argument ids
$($arg),*
@ -38,9 +43,9 @@ pub trait EdnCommand<C>: Command<C> {
$(let $arg: Option<$type> = EdnProvide::<$type>::get($state, $arg);)?
)*
//$(edn_command!(@bind $state => $arg $(?)? : $type);)*
return $command
return Some($command)
})*
panic!("no such command")
None
}
}
};

View file

@ -1,7 +1,7 @@
use crate::*;
pub trait EdnInput: Input {
fn matches (&self, token: &str) -> bool;
fn matches_edn (&self, token: &str) -> bool;
fn get_event <S: AsRef<str>> (_: &EdnItem<S>) -> Option<Self::Event> {
None
}

View file

@ -1,17 +1,17 @@
use crate::*;
use EdnItem::*;
pub struct EdnKeymap(pub Vec<EdnItem<String>>);
pub struct EdnKeymap<'a>(pub Vec<EdnItem<&'a str>>);
impl EdnKeymap {
impl<'a> EdnKeymap<'a> {
pub fn command <C, D: Command<C>, E: EdnCommand<C>, I: EdnInput> (
&self, state: &C, input: &I
) -> Option<E> {
for item in self.0.iter() {
if let Exp(items) = item {
match items.as_slice() {
[Sym(a), b, c @ ..] => if input.matches(a.as_str()) {
return Some(E::from_edn(state, &b.to_ref(), c))
[Sym(a), b, c @ ..] => if input.matches_edn(a) {
return E::from_edn(state, &b.to_ref(), c)
},
_ => unreachable!()
}
@ -23,9 +23,10 @@ impl EdnKeymap {
}
}
impl<T: AsRef<str>> From<T> for EdnKeymap {
fn from (source: T) -> Self {
Self(EdnItem::<String>::read_all(source.as_ref()).expect("failed to load keymap"))
impl<'a> From<&'a str> for EdnKeymap<'a> {
fn from (source: &'a str) -> Self {
let items = EdnItem::<&'a str>::read_all(source.as_ref());
Self(items.expect("failed to load keymap"))
}
}

View file

@ -1,61 +0,0 @@
use crate::*;
use std::sync::{Mutex, Arc, RwLock};
/// Implement the [Handle] trait.
#[macro_export] macro_rules! handle {
(|$self:ident:$Struct:ty,$input:ident|$handler:expr) => {
impl<E: Engine> Handle<E> for $Struct {
fn handle (&mut $self, $input: &E) -> Perhaps<E::Handled> {
$handler
}
}
};
($E:ty: |$self:ident:$Struct:ty,$input:ident|$handler:expr) => {
impl Handle<$E> for $Struct {
fn handle (&mut $self, $input: &$E) -> Perhaps<<$E as Input>::Handled> {
$handler
}
}
}
}
/// Handle input
pub trait Handle<E: Input>: Send + Sync {
fn handle (&mut self, _input: &E) -> Perhaps<E::Handled> {
Ok(None)
}
}
impl<E: Input, H: Handle<E>> Handle<E> for &mut H {
fn handle (&mut self, context: &E) -> Perhaps<E::Handled> {
(*self).handle(context)
}
}
impl<E: Input, H: Handle<E>> Handle<E> for Option<H> {
fn handle (&mut self, context: &E) -> Perhaps<E::Handled> {
if let Some(ref mut handle) = self {
handle.handle(context)
} else {
Ok(None)
}
}
}
impl<H, E: Input> Handle<E> for Mutex<H> where H: Handle<E> {
fn handle (&mut self, context: &E) -> Perhaps<E::Handled> {
self.get_mut().unwrap().handle(context)
}
}
impl<H, E: Input> Handle<E> for Arc<Mutex<H>> where H: Handle<E> {
fn handle (&mut self, context: &E) -> Perhaps<E::Handled> {
self.lock().unwrap().handle(context)
}
}
impl<H, E: Input> Handle<E> for RwLock<H> where H: Handle<E> {
fn handle (&mut self, context: &E) -> Perhaps<E::Handled> {
self.write().unwrap().handle(context)
}
}
impl<H, E: Input> Handle<E> for Arc<RwLock<H>> where H: Handle<E> {
fn handle (&mut self, context: &E) -> Perhaps<E::Handled> {
self.write().unwrap().handle(context)
}
}

View file

@ -1,9 +1,12 @@
use crate::*;
use std::sync::{Mutex, Arc, RwLock};
/// Event source
pub trait Input: Send + Sync + Sized {
/// Type of input event
type Event;
/// Result of handling input
type Handled;
type Handled; // TODO: make this an Option<Box dyn Command<Self>> containing the undo
/// Currently handled event
fn event (&self) -> &Self::Event;
/// Whether component should exit
@ -11,3 +14,62 @@ pub trait Input: Send + Sync + Sized {
/// Mark component as done
fn done (&self);
}
/// Implement the [Handle] trait.
#[macro_export] macro_rules! handle {
(|$self:ident:$Struct:ty,$input:ident|$handler:expr) => {
impl<E: Engine> Handle<E> for $Struct {
fn handle (&mut $self, $input: &E) -> Perhaps<E::Handled> {
$handler
}
}
};
($E:ty: |$self:ident:$Struct:ty,$input:ident|$handler:expr) => {
impl Handle<$E> for $Struct {
fn handle (&mut $self, $input: &$E) -> Perhaps<<$E as Input>::Handled> {
$handler
}
}
}
}
/// Handle input
pub trait Handle<E: Input>: Send + Sync {
fn handle (&mut self, _input: &E) -> Perhaps<E::Handled> {
Ok(None)
}
}
impl<E: Input, H: Handle<E>> Handle<E> for &mut H {
fn handle (&mut self, context: &E) -> Perhaps<E::Handled> {
(*self).handle(context)
}
}
impl<E: Input, H: Handle<E>> Handle<E> for Option<H> {
fn handle (&mut self, context: &E) -> Perhaps<E::Handled> {
if let Some(ref mut handle) = self {
handle.handle(context)
} else {
Ok(None)
}
}
}
impl<H, E: Input> Handle<E> for Mutex<H> where H: Handle<E> {
fn handle (&mut self, context: &E) -> Perhaps<E::Handled> {
self.get_mut().unwrap().handle(context)
}
}
impl<H, E: Input> Handle<E> for Arc<Mutex<H>> where H: Handle<E> {
fn handle (&mut self, context: &E) -> Perhaps<E::Handled> {
self.lock().unwrap().handle(context)
}
}
impl<H, E: Input> Handle<E> for RwLock<H> where H: Handle<E> {
fn handle (&mut self, context: &E) -> Perhaps<E::Handled> {
self.write().unwrap().handle(context)
}
}
impl<H, E: Input> Handle<E> for Arc<RwLock<H>> where H: Handle<E> {
fn handle (&mut self, context: &E) -> Perhaps<E::Handled> {
self.write().unwrap().handle(context)
}
}

View file

@ -2,7 +2,6 @@
//mod component; pub use self::component::*;
mod input; pub use self::input::*;
mod handle; pub use self::handle::*;
mod command; pub use self::command::*;
mod event_map; pub use self::event_map::*;