use proc_macro2::{TokenStream, TokenTree};
use syn::parse::ParseStream;
use super::ActionGroup;
pub struct GroupDeterminer {
group_type: Option<ActionGroup>,
check_input_fn: Box<dyn Fn(ParseStream) -> bool>,
check_parsed_fn: Option<Box<dyn Fn(TokenStream) -> bool>>,
length: usize,
}
#[macro_export]
macro_rules! instant_and_deferred_determiners {
($($group_type: ident => $($tokens: expr),+; $length: expr),+) => {
vec![
$(
crate::group_determiner!(
crate::expr_chain::ActionGroup::Instant(crate::expr_chain::CommandGroup::$group_type) => $($tokens),+; $length
),
crate::group_determiner!(
crate::expr_chain::ActionGroup::Deferred(crate::expr_chain::CommandGroup::$group_type) => Token![~], $($tokens),+; $length + 1
)
),*
]
};
}
#[macro_export]
macro_rules! tokens_checker {
($token1: expr, $token2:expr, $token3:expr) => {
Box::new(|input: ::syn::parse::ParseStream<'_>| {
input.peek($token1) && input.peek2($token2) && input.peek3($token3)
})
};
($token1: expr, $token2:expr) => {
Box::new(|input: ::syn::parse::ParseStream<'_>| input.peek($token1) && input.peek2($token2))
};
($token: expr) => {
Box::new(|input: ::syn::parse::ParseStream<'_>| input.peek($token))
};
}
#[macro_export]
macro_rules! group_determiner {
($group_type: expr => $($tokens: expr),+; $length: expr; $check_parsed_fn: expr) => {{
let check_input_fn = crate::tokens_checker!($($tokens),*);
crate::expr_chain::GroupDeterminer::new(
$group_type,
check_input_fn,
Some($check_parsed_fn),
$length
)
}};
($group_type: expr => $($tokens: expr),+; $length: expr) => {
crate::group_determiner!(
$group_type => $($tokens),+; $length; Box::new(is_valid_expr)
)
};
}
impl GroupDeterminer {
pub fn new(
group_type: impl Into<Option<ActionGroup>>,
check_input_fn: Box<dyn Fn(ParseStream) -> bool>,
check_parsed_fn: Option<Box<dyn Fn(TokenStream) -> bool>>,
length: usize,
) -> Self {
GroupDeterminer {
group_type: group_type.into(),
check_input_fn,
check_parsed_fn,
length,
}
}
pub fn get_group_type(&self) -> Option<ActionGroup> {
self.group_type
}
pub fn check_input(&self, input: ParseStream) -> bool {
(self.check_input_fn)(input)
}
pub fn check_parsed(&self, input: TokenStream) -> bool {
self.check_parsed_fn
.as_ref()
.map(|checker| checker(input))
.unwrap_or(true)
}
pub fn erase_input<'a>(&self, input: ParseStream<'a>) -> syn::Result<ParseStream<'a>> {
(0..self.length()).try_for_each(|_| input.parse::<TokenTree>().map(|_| ()))?;
Ok(input)
}
pub fn length(&self) -> usize {
self.length
}
}
#[cfg(test)]
mod tests {
use super::super::super::utils::is_valid_expr;
use super::super::*;
use super::*;
#[test]
fn it_creates_comma_determiner() {
let first_comma_determiner = GroupDeterminer::new(
None, Box::new(|input| input.peek(::syn::Token![,])),
None,
1,
);
assert_eq!(first_comma_determiner.get_group_type(), None);
assert!(first_comma_determiner.check_parsed(::quote::quote! { , }));
assert_eq!(first_comma_determiner.length(), 1);
}
#[test]
fn it_creates_then_determiner() {
let then_determiner = GroupDeterminer::new(
Some(ActionGroup::Instant(CommandGroup::Then)), Box::new(|input| input.peek(::syn::Token![=>])),
Some(Box::new(is_valid_expr)),
2,
);
assert_eq!(
then_determiner.get_group_type(),
Some(ActionGroup::Instant(CommandGroup::Then))
);
assert!(then_determiner.check_parsed(::quote::quote! { 23 }));
assert_eq!(then_determiner.length(), 2);
}
}