diff --git a/input/src/edn_cmd.rs b/input/src/edn_cmd.rs deleted file mode 100644 index 5bee02e3..00000000 --- a/input/src/edn_cmd.rs +++ /dev/null @@ -1,44 +0,0 @@ -use crate::*; -use EdnItem::*; -use std::marker::PhantomData; - -/// Turns an EDN symbol sequence into a command enum variant. -pub trait EdnCommand: Command { - fn from_edn <'a> (state: &C, head: &EdnItem<&str>, tail: &'a [EdnItem]) -> Self; - fn get_isize (state: &C, item: &EdnItem<&str>) -> Option { - None - } - fn get_usize (state: &C, item: &EdnItem<&str>) -> Option { - None - } - fn get_bool (state: &C, item: &EdnItem<&str>) -> Option { - None - } -} - -pub trait EdnInput: Input { - fn matches (&self, token: &str) -> bool; - fn get_event > (_: &EdnItem) -> Option { - None - } -} - -pub struct EdnKeymap(pub Vec>); - -impl EdnKeymap { - pub fn command , E: EdnCommand, I: EdnInput> (&self, state: &C, input: &I) -> Option { - 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)) - }, - _ => unreachable!() - } - } else { - unreachable!() - } - } - None - } -} diff --git a/input/src/edn_command.rs b/input/src/edn_command.rs new file mode 100644 index 00000000..2c735ff3 --- /dev/null +++ b/input/src/edn_command.rs @@ -0,0 +1,34 @@ +use crate::*; +use EdnItem::*; + +#[macro_export] macro_rules! edn_command { + ($Command:ty : |$state:ident:$State:ty| { + $(($key:literal [$($arg:ident : $type:ty),*] $command:expr))* + }) => { + impl EdnCommand<$State> for $Command { + fn from_edn <'a> ($state: &$State, head: &EdnItem<&str>, tail: &'a [EdnItem]) -> Self { + match (head, tail) { + $((EdnItem::Key($key), [$($arg),*]) => { + $(let $arg: $type = EdnProvide::<$type>::get_or_fail($state, $arg);)* + $command + },)* + _ => panic!("no such command") + } + } + } + } +} + +/// Turns an EDN symbol sequence into a command enum variant. +pub trait EdnCommand: Command { + fn from_edn <'a> (state: &C, head: &EdnItem<&str>, tail: &'a [EdnItem]) -> Self; + fn get_isize (state: &C, item: &EdnItem<&str>) -> Option { + None + } + fn get_usize (state: &C, item: &EdnItem<&str>) -> Option { + None + } + fn get_bool (state: &C, item: &EdnItem<&str>) -> Option { + None + } +} diff --git a/input/src/edn_input.rs b/input/src/edn_input.rs new file mode 100644 index 00000000..2a0f8558 --- /dev/null +++ b/input/src/edn_input.rs @@ -0,0 +1,9 @@ +use crate::*; +use EdnItem::*; + +pub trait EdnInput: Input { + fn matches (&self, token: &str) -> bool; + fn get_event > (_: &EdnItem) -> Option { + None + } +} diff --git a/input/src/edn_keymap.rs b/input/src/edn_keymap.rs new file mode 100644 index 00000000..49396959 --- /dev/null +++ b/input/src/edn_keymap.rs @@ -0,0 +1,22 @@ +use crate::*; +use EdnItem::*; + +pub struct EdnKeymap(pub Vec>); + +impl EdnKeymap { + pub fn command , E: EdnCommand, I: EdnInput> (&self, state: &C, input: &I) -> Option { + 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)) + }, + _ => unreachable!() + } + } else { + unreachable!() + } + } + None + } +} diff --git a/input/src/edn_provide.rs b/input/src/edn_provide.rs new file mode 100644 index 00000000..7af0d397 --- /dev/null +++ b/input/src/edn_provide.rs @@ -0,0 +1,25 @@ +use crate::*; + +/// Implement `EdnProvide` for a type and context +#[macro_export] macro_rules! edn_provide { + ($type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => { + impl EdnProvide<$type> for $State { + fn get > (&$self, edn: &EdnItem) -> Option<$type> { + Some(match edn.to_ref() { + $(EdnItem::Sym($pat) => $expr),*, + _ => return None + }) + } + } + } +} + +/// Map EDN tokens to parameters of a given type for a given context +pub trait EdnProvide { + fn get > (&self, _edn: &EdnItem) -> Option { + None + } + fn get_or_fail > (&self, edn: &EdnItem) -> U { + self.get(edn).expect("no value") + } +} diff --git a/input/src/lib.rs b/input/src/lib.rs index c96dfe48..c9eabac0 100644 --- a/input/src/lib.rs +++ b/input/src/lib.rs @@ -5,7 +5,11 @@ 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::*; -mod edn_cmd; pub use self::edn_cmd::*; + +mod edn_command; pub use self::edn_command::*; +mod edn_input; pub use self::edn_input::*; +mod edn_keymap; pub use self::edn_keymap::*; +mod edn_provide; pub use self::edn_provide::*; pub(crate) use ::tek_edn::EdnItem; /// Standard error trait. diff --git a/midi/src/midi_edit_cmd.rs b/midi/src/midi_edit_cmd.rs index 8c3926c4..39628e3d 100644 --- a/midi/src/midi_edit_cmd.rs +++ b/midi/src/midi_edit_cmd.rs @@ -1,22 +1,37 @@ use crate::*; use self::MidiEditCommand::*; use KeyCode::*; -impl EdnCommand for MidiEditCommand { - fn from_edn <'a> (state: &MidiEditor, head: &EdnItem<&str>, tail: &'a [EdnItem]) -> Self { - use EdnItem::*; - match (head, tail) { - (Key("note/put"), [a]) => Self::PutNote, - (Key("note/del"), [a]) => Self::AppendNote, - (Key("note/dur"), [a]) => Self::AppendNote, - (Key("note/range"), [a]) => Self::AppendNote, - (Key("note/pos"), [a]) => Self::AppendNote, - (Key("time/pos"), [a]) => Self::AppendNote, - (Key("time/zoom"), [a]) => Self::AppendNote, - (Key("time/lock"), [a]) => Self::AppendNote, - _ => todo!() - } - } -} +edn_provide!(bool: |self: MidiEditor|{ + ":true" => true, + ":false" => false +}); +edn_provide!(usize: |self: MidiEditor|{ + ":note-length" => self.note_len(), + ":note-point" => self.note_point(), + ":time-point" => self.time_point(), + ":time-zoom" => self.time_zoom().get(), +}); +edn_command!(MidiEditCommand: |state: MidiEditor| { + ("note/put" [a: bool] Self::PutNote) + ("note/del" [a: bool] Self::PutNote) + ("note/dur" [a: usize] Self::SetNoteCursor(a)) +}); +//impl EdnCommand for MidiEditCommand { + //fn from_edn <'a> (state: &MidiEditor, head: &EdnItem<&str>, tail: &'a [EdnItem]) -> Self { + //use EdnItem::*; + //match (head, tail) { + //(Key("note/put"), [a]) => Self::PutNote, + //(Key("note/del"), [a]) => Self::AppendNote, + //(Key("note/dur"), [a]) => Self::AppendNote, + //(Key("note/range"), [a]) => Self::AppendNote, + //(Key("note/pos"), [a]) => Self::AppendNote, + //(Key("time/pos"), [a]) => Self::AppendNote, + //(Key("time/zoom"), [a]) => Self::AppendNote, + //(Key("time/lock"), [a]) => Self::AppendNote, + //_ => todo!() + //} + //} +//} #[derive(Clone, Debug)] pub enum MidiEditCommand { // TODO: 1-9 seek markers that by default start every 8th of the clip AppendNote,