use crate::*; /** Implement `Command` for given `State` and collection * of `Variant` to `handler` mappings. */ #[macro_export] macro_rules! defcom { ([$self:ident, $state:ident:$State:ty] $(($Command:ident $(( $Variant:ident [$($($param:ident: $Param:ty),+)?] $expr:expr ))*))*) => { $(#[derive(Clone, Debug)] pub enum $Command { $($Variant $(($($Param),+))?),* })* $(command!(|$self: $Command, $state: $State|match $self { $($Command::$Variant $(($($param),+))? => $expr),* });)* }; (|$self:ident, $state:ident:$State:ty| $($Command:ident { $( $Variant:ident $(($($param:ident: $Param:ty),+))? => $expr:expr )* $(,)? })*) => { $(#[derive(Clone, Debug)] pub enum $Command { $($Variant $(($($Param),+))?),* })* $(command!(|$self: $Command, $state: $State|match $self { $($Command::$Variant $(($($param),+))? => $expr),* });)* }; } /** Implement `Command` for given `State` and `handler` */ #[macro_export] macro_rules! command { ($(<$($l:lifetime),+>)?|$self:ident:$Command:ty,$state:ident:$State:ty|$handler:expr) => { impl$(<$($l),+>)? Command<$State> for $Command { fn execute ($self, $state: &mut $State) -> Perhaps { Ok($handler) } } }; } /** Implement `DslCommand` for given `State` and `Command` */ #[cfg(feature = "dsl")] #[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 Dsl $( // type:name separator : // argument type $type:ty )? ),* // rest of parameters $(, ..$rest:ident)? ] // bound command: $command:expr ))* }) => { impl<'a, $State: $Trait> TryFromDsl<'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 $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 Dsl $( // type:name separator : // argument type $type:ty )? ),* // rest of parameters $(, ..$rest:ident)? ] // bound command: $command:expr ))* }) => { impl<'a> TryFromDsl<'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 $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); }; }