yew_callback/
lib.rs

1use quote::ToTokens;
2
3#[proc_macro]
4pub fn callback(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
5    let clone = syn::parse_macro_input!(input as Callback);
6    clone.into_token_stream().into()
7}
8
9struct Callback {
10    captures: Vec<Capture>,
11    body: Closure,
12}
13
14impl syn::parse::Parse for Callback {
15    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
16        let mut captures = Vec::new();
17
18        while let Ok(capture) = Capture::parse(input) {
19            captures.push(capture);
20
21            if input.peek(syn::Token![,]) {
22                input.parse::<syn::Token![,]>()?;
23            }
24        }
25
26        let body = input.parse::<Closure>()?;
27
28        if input.peek(syn::Token![,]) {
29            input.parse::<syn::Token![,]>()?;
30        }
31
32        Ok(Self { captures, body })
33    }
34}
35
36impl ToTokens for Callback {
37    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
38        let captures = &self.captures;
39        let cb = &self.body;
40
41        tokens.extend(quote::quote! {{
42            #( #captures )*
43            yew::Callback::from(#cb)
44        }});
45    }
46}
47
48struct Capture {
49    pub alias: Option<syn::Ident>,
50    pub name: syn::Expr,
51}
52
53impl syn::parse::Parse for Capture {
54    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
55        let name = input.parse::<syn::Ident>()?;
56
57        let (alias, name) = if input.peek(syn::Token![=]) {
58            input.parse::<syn::Token![=]>()?;
59            let expr = input.parse::<syn::Expr>()?;
60            (Some(name), expr)
61        } else {
62            let mut segments = syn::punctuated::Punctuated::new();
63            segments.push_value(name.into());
64
65            let path = syn::Path {
66                leading_colon: None,
67                segments,
68            };
69            let exprpath = syn::ExprPath {
70                attrs: Vec::new(),
71                qself: None,
72                path,
73            };
74
75            (None, syn::Expr::Path(exprpath))
76        };
77
78        Ok(Self { alias, name })
79    }
80}
81
82impl ToTokens for Capture {
83    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
84        let name = &self.name;
85
86        let token = if let Some(alias) = &self.alias {
87            quote::quote! { let #alias = #name.clone(); }
88        } else {
89            quote::quote! { let #name = #name.clone(); }
90        };
91
92        tokens.extend(token);
93    }
94}
95
96struct Closure {
97    cb: syn::ExprClosure,
98}
99
100impl syn::parse::Parse for Closure {
101    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
102        Ok(Self { cb: input.parse()? })
103    }
104}
105
106impl ToTokens for Closure {
107    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
108        self.cb.to_tokens(tokens);
109    }
110}