use crate::*; /// [Input] state that can be matched against a [Value]. pub trait AtomInput: Input { fn matches_atom (&self, token: &str) -> bool; } pub trait KeyMap<'a> { /// Try to find a command that matches the current input event. fn command , I: AtomInput> (&'a self, state: &'a S, input: &'a I) -> Option; } impl<'a> KeyMap<'a> for SourceIter<'a> { fn command , I: AtomInput> (&'a self, state: &'a S, input: &'a I) -> Option { let mut iter = self.clone(); while let Some((token, rest)) = iter.next() { iter = rest; match token { Token { value: Value::Exp(0, exp_iter), .. } => { let mut exp_iter = exp_iter.clone(); match exp_iter.next() { Some(Token { value: Value::Sym(binding), .. }) => { if input.matches_atom(binding) { if let Some(command) = C::try_from_expr(state, exp_iter.clone()) { return Some(command) } } }, _ => panic!("invalid config (expected symbol)") } }, _ => panic!("invalid config (expected expression)") } } None } } impl<'a> KeyMap<'a> for TokenIter<'a> { fn command , I: AtomInput> (&'a self, state: &'a S, input: &'a I) -> Option { let mut iter = self.clone(); while let Some(next) = iter.next() { match next { Token { value: Value::Exp(0, exp_iter), .. } => { let mut exp_iter = exp_iter.clone(); match exp_iter.next() { Some(Token { value: Value::Sym(binding), .. }) => { if input.matches_atom(binding) { if let Some(command) = C::try_from_expr(state, exp_iter.clone()) { return Some(command) } } }, _ => panic!("invalid config (expected symbol)") } }, _ => panic!("invalid config (expected expression)") } } None } } /// A [Command] that can be constructed from a [Token]. pub trait AtomCommand<'a, C>: TryFromAtom<'a, C> + Command {} impl<'a, C, T: TryFromAtom<'a, C> + Command> AtomCommand<'a, C> for T {} /** Implement `AtomCommand` for given `State` and `Command` */ #[macro_export] macro_rules! atom_command { ($Command:ty : |$state:ident:<$State:ident: $Trait:path>| { $(( // identifier $key:literal [ // named parameters $( // argument name $arg:ident // if type is not provided defaults to Atom $( // type:name separator : // argument type $type:ty )? ),* // rest of parameters $(, ..$rest:ident)? ] // bound command: $command:expr ))* }) => { impl<'a, $State: $Trait> TryFromAtom<'a, $State> for $Command { fn try_from_expr ($state: &$State, iter: TokenIter) -> Option { let iter = iter.clone(); match iter.next() { $(Some(Token { value: Value::Key($key), .. }) => { let iter = iter.clone(); $( let next = iter.next(); if next.is_none() { panic!("no argument: {}", stringify!($arg)); } let $arg = next.unwrap(); $(let $arg: Option<$type> = Context::<$type>::get($state, &$arg.value);)? )* $(let $rest = iter.clone();)? return Some($command) },)* _ => None } None } } }; ($Command:ty : |$state:ident:$State:ty| { $(( // identifier $key:literal [ // named parameters $( // argument name $arg:ident // if type is not provided defaults to Atom $( // type:name separator : // argument type $type:ty )? ),* // rest of parameters $(, ..$rest:ident)? ] // bound command: $command:expr ))* }) => { impl<'a> TryFromAtom<'a, $State> for $Command { fn try_from_expr ($state: &$State, iter: TokenIter) -> Option { let mut iter = iter.clone(); match iter.next() { $(Some(Token { value: Value::Key($key), .. }) => { let mut iter = iter.clone(); $( let next = iter.next(); if next.is_none() { panic!("no argument: {}", stringify!($arg)); } let $arg = next.unwrap(); $(let $arg: Option<$type> = Context::<$type>::get($state, &$arg.value);)? )* $(let $rest = iter.clone();)? return Some($command) }),* _ => None } } } }; (@bind $state:ident =>$arg:ident ? : $type:ty) => { let $arg: Option<$type> = Context::<$type>::get($state, $arg); }; (@bind $state:ident => $arg:ident : $type:ty) => { let $arg: $type = Context::<$type>::get_or_fail($state, $arg); }; } //pub struct SourceKeyMap<'a>(&'a str); //impl<'a> KeyMap for SourceKeyMap<'a> { //fn command > (&self, state: &S, input: &AtomInput) -> Option { //todo!(); //None //} //} //pub struct ParsedKeyMap<'a>(TokensIterator<'a>); //impl<'a> KeyMap for ParsedKeyMap<'a> { //fn command > (&self, state: &S, input: &AtomInput) -> Option { //todo!(); //None //} //} //pub struct RefKeyMap<'a>(TokensIterator<'a>); //impl<'a> KeyMap for RefKeyMap<'a> { //fn command > (&self, state: &S, input: &AtomInput) -> Option { //todo!(); ////for token in self.0 { ////match token?.kind() { ////TokenKind::Exp => match atoms.as_slice() { ////[key, command, args @ ..] => match (key.kind(), key.text()) { ////(TokenKind::Sym, key) => { ////if input.matches_atom(key) { ////let command = C::from_atom(state, command, args); ////if command.is_some() { ////return command ////} ////} ////}, ////_ => panic!("invalid config: {item}") ////}, ////_ => panic!("invalid config: {item}") ////} ////_ => panic!("invalid config: {item}") ////} ////} //None //} //} //pub struct ArcKeyMap(Vec); //impl KeyMap for ArcKeyMap { //fn command > (&self, state: &S, input: &AtomInput) -> Option { //for atom in self.0.iter() { //match atom { //ArcAtom::Exp(atoms) => match atoms.as_slice() { //[key, command, args @ ..] => match (key.kind(), key.text()) { //(TokenKind::Sym, key) => { //if input.matches_atom(key) { //let command = C::from_atom(state, command, args); //if command.is_some() { //return command //} //} //}, //_ => panic!("invalid config: {atom}") //}, //_ => panic!("invalid config: {atom}") //} //_ => panic!("invalid config: {atom}") //} //} //None //} //} #[cfg(test)] #[test] fn test_atom_keymap () -> Usually<()> { let keymap = SourceIter::new(""); Ok(()) }