tengri/input/src/input_macros.rs
unspeaker 35ad371205
Some checks are pending
/ build (push) Waiting to run
input: add InputMap; dsl/output/tui: Atom->Dsl
2025-04-28 04:55:27 +03:00

131 lines
4.7 KiB
Rust

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<Self> {
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<Self> {
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<Self> {
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);
};
}