closure_pass/
lib.rs

1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use quote::quote;
5use syn::parse::{Parse, ParseStream};
6use syn::{parse_macro_input, Expr, Ident, Result, Token};
7
8enum Arg {
9    Item(Ident),
10    ExprItem(Ident, Expr),
11}
12
13impl Parse for Arg {
14    fn parse(input: ParseStream) -> Result<Self> {
15        let ident: Ident = input.parse()?;
16
17        let la = input.lookahead1();
18
19        if la.peek(Token![=]) {
20            let _: Token![=] = input.parse()?;
21            Ok(Arg::ExprItem(ident, input.parse()?))
22        } else {
23            Ok(Arg::Item(ident))
24        }
25    }
26}
27
28struct Args(Vec<Arg>);
29
30impl Args {
31    fn clone_items(&self) -> impl Iterator<Item = &Ident> + '_ {
32        self.0.iter().filter_map(|arg| {
33            if let Arg::Item(ident) = arg {
34                Some(ident)
35            } else {
36                None
37            }
38        })
39    }
40
41    fn arbitrary_exprs(&self) -> impl Iterator<Item = (&Ident, &Expr)> + '_ {
42        self.0.iter().filter_map(|arg| {
43            if let Arg::ExprItem(ident, expr) = arg {
44                Some((ident, expr))
45            } else {
46                None
47            }
48        })
49    }
50}
51
52impl Parse for Args {
53    fn parse(input: ParseStream) -> Result<Self> {
54        if input.is_empty() {
55            return Ok(Self(Vec::new()));
56        }
57
58        let terms = input.parse_terminated::<_, Token![,]>(Arg::parse)?;
59
60        Ok(Self(terms.into_iter().collect()))
61    }
62}
63
64#[proc_macro_attribute]
65pub fn closure_pass(args: TokenStream, input: TokenStream) -> TokenStream {
66    let args = parse_macro_input!(args as Args);
67    let input = parse_macro_input!(input as Expr);
68
69    let clone_items = args
70        .clone_items()
71        .map(|ident| {
72            quote! {
73                let #ident = #ident.clone();
74            }
75        })
76        .collect::<Vec<_>>();
77
78    let arbitrary_exprs = args
79        .arbitrary_exprs()
80        .map(|(ident, expr)| {
81            quote! {
82                let #ident = #expr;
83            }
84        })
85        .collect::<Vec<_>>();
86
87    let processed = quote! {{
88        #(#clone_items)*
89        #(#arbitrary_exprs)*
90        #input
91    }};
92
93    TokenStream::from(processed)
94}