glib_macros/
closure.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use proc_macro2::{Ident, Span, TokenStream};
4use quote::{quote, ToTokens};
5use syn::{
6    parse::{Parse, ParseStream},
7    spanned::Spanned,
8    Attribute, ExprClosure, Token,
9};
10
11use crate::{
12    clone::{Capture, CaptureKind, UpgradeBehaviour},
13    utils::crate_ident_new,
14};
15
16struct Closure {
17    captures: Vec<Capture>,
18    args: Vec<Ident>,
19    upgrade_behaviour: UpgradeBehaviour,
20    closure: ExprClosure,
21    constructor: &'static str,
22}
23
24impl Parse for Closure {
25    fn parse(input: ParseStream) -> syn::Result<Self> {
26        let mut captures: Vec<Capture> = vec![];
27        let mut upgrade_behaviour: Option<(UpgradeBehaviour, Span)> = None;
28
29        loop {
30            // There must either be one or no attributes here. Multiple attributes are not
31            // supported.
32            //
33            // If this is a capture attribute, it must be followed by an identifier.
34            // If this is an upgrade failure attribute, it might be followed by a closure. After the
35            // upgrade failure attribute there must not be any further attributes.
36            //
37            // If this is not an attribute then it is a closure, async closure or async block which
38            // is handled outside the loop
39            let attrs = input.call(Attribute::parse_outer)?;
40            if attrs.is_empty() {
41                break;
42            };
43
44            if let Some(capture) = Capture::maybe_parse(&attrs, input)? {
45                if capture.kind == CaptureKind::Watch
46                    && captures.iter().any(|c| c.kind == CaptureKind::Watch)
47                {
48                    return Err(syn::Error::new_spanned(
49                        &attrs[0],
50                        "only one `watch` capture is allowed per closure",
51                    ));
52                }
53
54                captures.push(capture);
55            } else if let Some(behaviour) = UpgradeBehaviour::maybe_parse(&attrs, input)? {
56                if upgrade_behaviour.is_some() {
57                    return Err(syn::Error::new_spanned(
58                        &attrs[0],
59                        "multiple upgrade failure attributes are not supported",
60                    ));
61                }
62
63                upgrade_behaviour = Some((behaviour, attrs[0].span()));
64                break;
65            } else if let Some(ident) = attrs[0].path().get_ident() {
66                return Err(syn::Error::new_spanned(
67                        &attrs[0],
68                        format!(
69                            "unsupported attribute `{ident}`: only `watch`, `strong`, `weak`, `weak_allow_none`, `to_owned`, `upgrade_or`, `upgrade_or_else`, `upgrade_or_default` and `upgrade_or_panic` are supported",
70                        ),
71                ));
72            } else {
73                return Err(syn::Error::new_spanned(
74                        &attrs[0],
75                        "unsupported attribute: only `strong`, `weak`, `weak_allow_none`, `to_owned`, `upgrade_or_else`, `upgrade_or_default` and `upgrade_or_panic` are supported",
76                ));
77            }
78        }
79
80        if let Some((_, ref span)) = upgrade_behaviour {
81            if captures.iter().all(|c| c.kind != CaptureKind::Weak) {
82                return Err(syn::Error::new(
83                    *span,
84                    "upgrade failure attribute can only be used together with weak variable captures",
85                ));
86            }
87        }
88
89        let upgrade_behaviour = upgrade_behaviour.map(|x| x.0).unwrap_or_default();
90
91        let mut closure = input.parse::<ExprClosure>()?;
92        if closure.asyncness.is_some() {
93            return Err(syn::Error::new_spanned(
94                closure,
95                "async closures not supported",
96            ));
97        }
98        if !captures.is_empty() && closure.capture.is_none() {
99            return Err(syn::Error::new_spanned(
100                closure,
101                "closures need to capture variables by move. Please add the `move` keyword",
102            ));
103        }
104        closure.capture = None;
105
106        let args = closure
107            .inputs
108            .iter()
109            .enumerate()
110            .map(|(i, _)| Ident::new(&format!("____value{i}"), Span::call_site()))
111            .collect();
112
113        // Trailing comma, if any
114        if input.peek(Token![,]) {
115            input.parse::<Token![,]>()?;
116        }
117
118        Ok(Closure {
119            captures,
120            args,
121            upgrade_behaviour,
122            closure,
123            constructor: "new",
124        })
125    }
126}
127
128impl ToTokens for Closure {
129    fn to_tokens(&self, tokens: &mut TokenStream) {
130        let crate_ident = crate_ident_new();
131
132        let closure_ident = Ident::new("____closure", Span::call_site());
133        let values_ident = Ident::new("____values", Span::call_site());
134        let upgrade_failure_closure_ident =
135            Ident::new("____upgrade_failure_closure", Span::call_site());
136        let upgrade_failure_closure_wrapped_ident =
137            Ident::new("____upgrade_failure_closure_wrapped", Span::call_site());
138
139        let outer_before = self
140            .captures
141            .iter()
142            .map(|c| c.outer_before_tokens(&crate_ident));
143        let inner_before = self.captures.iter().map(|c| {
144            c.inner_before_tokens(
145                &crate_ident,
146                &self.upgrade_behaviour,
147                &upgrade_failure_closure_wrapped_ident,
148                Some(quote! {
149                    return #crate_ident::closure::IntoClosureReturnValue::into_closure_return_value(());
150                }),
151            )
152        });
153        let outer_after = self
154            .captures
155            .iter()
156            .map(|c| c.outer_after_tokens(&crate_ident, &closure_ident));
157
158        let arg_values = self.args.iter().enumerate().map(|(index, arg)| {
159            let err_msg = format!("Wrong type for argument {index}: {{:?}}");
160            quote! {
161                let #arg = ::core::result::Result::unwrap_or_else(
162                    #crate_ident::Value::get(&#values_ident[#index]),
163                    |e| panic!(#err_msg, e),
164                );
165            }
166        });
167        let arg_names = &self.args;
168        let args_len = self.args.len();
169        let closure = &self.closure;
170        let constructor = Ident::new(self.constructor, Span::call_site());
171
172        let upgrade_failure_closure = match self.upgrade_behaviour {
173            UpgradeBehaviour::Default => Some(quote! {
174                let #upgrade_failure_closure_ident = ::std::default::Default::default;
175                let #upgrade_failure_closure_wrapped_ident = ||
176                    #crate_ident::closure::IntoClosureReturnValue::into_closure_return_value(
177                        (#upgrade_failure_closure_ident)()
178                    );
179            }),
180            UpgradeBehaviour::Expression(ref expr) => Some(quote! {
181                let #upgrade_failure_closure_ident = move || {
182                    #expr
183                };
184                let #upgrade_failure_closure_wrapped_ident = ||
185                    #crate_ident::closure::IntoClosureReturnValue::into_closure_return_value(
186                        (#upgrade_failure_closure_ident)()
187                    );
188            }),
189            UpgradeBehaviour::Closure(ref closure_2) => Some(quote! {
190                    let #upgrade_failure_closure_ident = #closure_2;
191                    let #upgrade_failure_closure_wrapped_ident = ||
192                        #crate_ident::closure::IntoClosureReturnValue::into_closure_return_value(
193                            (#upgrade_failure_closure_ident)()
194                        );
195            }),
196            _ => None,
197        };
198
199        let assert_return_type = upgrade_failure_closure.is_some().then(|| {
200            quote! {
201                fn ____same<T>(_a: &T, _b: impl Fn() -> T) {}
202                ____same(&____res, #upgrade_failure_closure_ident);
203            }
204        });
205
206        tokens.extend(quote! {
207            {
208                let #closure_ident = {
209                    #(#outer_before)*
210                    #crate_ident::closure::RustClosure::#constructor(move |#values_ident| {
211                        assert_eq!(
212                            #values_ident.len(),
213                            #args_len,
214                            "Expected {} arguments but got {}",
215                            #args_len,
216                            #values_ident.len(),
217                        );
218                        #upgrade_failure_closure
219                        #(#inner_before)*
220                        #(#arg_values)*
221                        #crate_ident::closure::IntoClosureReturnValue::into_closure_return_value({
222                            let ____res = (#closure)(#(#arg_names),*);
223                            #assert_return_type
224                            ____res
225                        })
226                    }
227                    )
228                };
229                #(#outer_after)*
230                #closure_ident
231            }
232        });
233    }
234}
235
236pub(crate) fn closure_inner(
237    input: proc_macro::TokenStream,
238    constructor: &'static str,
239) -> proc_macro::TokenStream {
240    let mut closure = syn::parse_macro_input!(input as Closure);
241    closure.constructor = constructor;
242    closure.into_token_stream().into()
243}