async_recursion/
parse.rs

1use proc_macro2::Span;
2use syn::{
3    parse::{Error, Parse, ParseStream, Result},
4    token::Question,
5    ItemFn, Token,
6};
7
8pub struct AsyncItem(pub ItemFn);
9
10impl Parse for AsyncItem {
11    fn parse(input: ParseStream) -> Result<Self> {
12        let item: ItemFn = input.parse()?;
13
14        // Check that this is an async function
15        if item.sig.asyncness.is_none() {
16            return Err(Error::new(Span::call_site(), "expected an async function"));
17        }
18
19        Ok(AsyncItem(item))
20    }
21}
22
23pub struct RecursionArgs {
24    pub send_bound: bool,
25    pub sync_bound: bool,
26}
27
28/// Custom keywords for parser
29mod kw {
30    syn::custom_keyword!(Send);
31    syn::custom_keyword!(Sync);
32}
33
34#[derive(Debug, PartialEq, Eq)]
35enum Arg {
36    NotSend,
37    Sync,
38}
39
40impl std::fmt::Display for Arg {
41    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42        match self {
43            Self::NotSend => write!(f, "?Send"),
44            Self::Sync => write!(f, "Sync"),
45        }
46    }
47}
48
49impl Parse for Arg {
50    fn parse(input: ParseStream) -> Result<Self> {
51        if input.peek(Token![?]) {
52            input.parse::<Question>()?;
53            input.parse::<kw::Send>()?;
54            Ok(Arg::NotSend)
55        } else {
56            input.parse::<kw::Sync>()?;
57            Ok(Arg::Sync)
58        }
59    }
60}
61
62impl Parse for RecursionArgs {
63    fn parse(input: ParseStream) -> Result<Self> {
64        let mut send_bound: bool = true;
65        let mut sync_bound: bool = false;
66
67        let args_parsed: Vec<Arg> =
68            syn::punctuated::Punctuated::<Arg, syn::Token![,]>::parse_terminated(input)
69                .map_err(|e| input.error(format!("failed to parse macro arguments: {e}")))?
70                .into_iter()
71                .collect();
72
73        // Avoid sloppy input
74        if args_parsed.len() > 2 {
75            return Err(Error::new(Span::call_site(), "received too many arguments"));
76        } else if args_parsed.len() == 2 && args_parsed[0] == args_parsed[1] {
77            return Err(Error::new(
78                Span::call_site(),
79                format!("received duplicate argument: `{}`", args_parsed[0]),
80            ));
81        }
82
83        for arg in args_parsed {
84            match arg {
85                Arg::NotSend => send_bound = false,
86                Arg::Sync => sync_bound = true,
87            }
88        }
89
90        Ok(Self {
91            send_bound,
92            sync_bound,
93        })
94    }
95}