glib_macros/
closure_old.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, TokenStreamExt};
5use syn::{ext::IdentExt, spanned::Spanned, Token};
6
7use crate::utils::crate_ident_new;
8
9#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10enum CaptureKind {
11    Watch,
12    WeakAllowNone,
13    Strong,
14    ToOwned,
15}
16
17struct Capture {
18    name: TokenStream,
19    alias: Option<syn::Ident>,
20    kind: CaptureKind,
21    start: Span,
22}
23
24impl Capture {
25    fn alias(&self) -> TokenStream {
26        if let Some(ref a) = self.alias {
27            a.to_token_stream()
28        } else {
29            self.name.to_token_stream()
30        }
31    }
32    fn outer_before_tokens(&self, crate_ident: &TokenStream) -> TokenStream {
33        let alias = self.alias();
34        let name = &self.name;
35        match self.kind {
36            CaptureKind::Watch => quote! {
37                let #alias = #crate_ident::object::Watchable::watched_object(&#name);
38            },
39            CaptureKind::WeakAllowNone => quote! {
40                let #alias = #crate_ident::clone::Downgrade::downgrade(&#name);
41            },
42            CaptureKind::Strong => quote! {
43                let #alias = #name.clone();
44            },
45            CaptureKind::ToOwned => quote! {
46                let #alias = ::std::borrow::ToOwned::to_owned(&*#name);
47            },
48        }
49    }
50
51    fn outer_after_tokens(&self, crate_ident: &TokenStream, closure_ident: &Ident) -> TokenStream {
52        let name = &self.name;
53        match self.kind {
54            CaptureKind::Watch => quote! {
55                #crate_ident::object::Watchable::watch_closure(&#name, &#closure_ident);
56            },
57            _ => Default::default(),
58        }
59    }
60
61    fn inner_before_tokens(&self, crate_ident: &TokenStream) -> TokenStream {
62        let alias = self.alias();
63        match self.kind {
64            CaptureKind::Watch => {
65                quote! {
66                    let #alias = unsafe { #alias.borrow() };
67                    let #alias = ::core::convert::AsRef::as_ref(&#alias);
68                }
69            }
70            CaptureKind::WeakAllowNone => quote! {
71                let #alias = #crate_ident::clone::Upgrade::upgrade(&#alias);
72            },
73            _ => Default::default(),
74        }
75    }
76}
77
78impl syn::parse::Parse for CaptureKind {
79    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
80        input.parse::<Token![@]>()?;
81        let mut idents = TokenStream::new();
82        idents.append(input.call(syn::Ident::parse_any)?);
83        while input.peek(Token![-]) {
84            input.parse::<Token![-]>()?;
85            idents.append(input.call(syn::Ident::parse_any)?);
86        }
87        let keyword = idents
88            .clone()
89            .into_iter()
90            .map(|i| i.to_string())
91            .collect::<Vec<_>>()
92            .join("-");
93        match keyword.as_str() {
94            "strong" => Ok(CaptureKind::Strong),
95            "watch" => Ok(CaptureKind::Watch),
96            "weak-allow-none" => Ok(CaptureKind::WeakAllowNone),
97            "to-owned" => Ok(CaptureKind::ToOwned),
98            k => Err(syn::Error::new(
99                idents.span(),
100                format!("Unknown keyword `{}`, only `watch`, `weak-allow-none`, `to-owned` and `strong` are allowed",
101                k),
102            )),
103        }
104    }
105}
106
107impl syn::parse::Parse for Capture {
108    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
109        let start = input.span();
110        let kind = input.parse()?;
111        let mut name = TokenStream::new();
112        name.append(input.call(syn::Ident::parse_any)?);
113        while input.peek(Token![.]) {
114            input.parse::<Token![.]>()?;
115            name.append(proc_macro2::Punct::new('.', proc_macro2::Spacing::Alone));
116            name.append(input.call(syn::Ident::parse_any)?);
117        }
118        let alias = if input.peek(Token![as]) {
119            input.parse::<Token![as]>()?;
120            input.parse()?
121        } else {
122            None
123        };
124        if alias.is_none() {
125            if name.to_string() == "self" {
126                return Err(syn::Error::new_spanned(
127                    name,
128                    "Can't use `self` as variable name. Try storing it in a temporary variable or \
129                    rename it using `as`.",
130                ));
131            }
132            if name.to_string().contains('.') {
133                return Err(syn::Error::new(
134                    name.span(),
135                    format!(
136                        "`{}`: Field accesses are not allowed as is, you must rename it!",
137                        name
138                    ),
139                ));
140            }
141        }
142        Ok(Capture {
143            name,
144            alias,
145            kind,
146            start,
147        })
148    }
149}
150
151struct Closure {
152    captures: Vec<Capture>,
153    args: Vec<Ident>,
154    closure: syn::ExprClosure,
155    constructor: &'static str,
156}
157
158impl syn::parse::Parse for Closure {
159    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
160        let mut captures: Vec<Capture> = vec![];
161        if input.peek(Token![@]) {
162            loop {
163                let capture = input.parse::<Capture>()?;
164                if capture.kind == CaptureKind::Watch {
165                    if let Some(existing) = captures.iter().find(|c| c.kind == CaptureKind::Watch) {
166                        return Err(syn::Error::new(
167                            existing.start,
168                            "Only one `@watch` capture is allowed per closure",
169                        ));
170                    }
171                }
172                captures.push(capture);
173                if input.peek(Token![,]) {
174                    input.parse::<Token![,]>()?;
175                    if !input.peek(Token![@]) {
176                        break;
177                    }
178                } else {
179                    break;
180                }
181            }
182        }
183        if !captures.is_empty() {
184            input.parse::<Token![=>]>()?;
185        }
186        let mut closure = input.parse::<syn::ExprClosure>()?;
187        if closure.asyncness.is_some() {
188            return Err(syn::Error::new_spanned(
189                closure,
190                "Async closure not allowed",
191            ));
192        }
193        if !captures.is_empty() && closure.capture.is_none() {
194            return Err(syn::Error::new_spanned(
195                closure,
196                "Closure with captures needs to be \"moved\" so please add `move` before closure",
197            ));
198        }
199        let args = closure
200            .inputs
201            .iter()
202            .enumerate()
203            .map(|(i, _)| Ident::new(&format!("____value{i}"), Span::call_site()))
204            .collect();
205        closure.capture = None;
206        Ok(Closure {
207            captures,
208            args,
209            closure,
210            constructor: "new",
211        })
212    }
213}
214
215impl ToTokens for Closure {
216    fn to_tokens(&self, tokens: &mut TokenStream) {
217        let closure_ident = Ident::new("____closure", Span::call_site());
218        let values_ident = Ident::new("____values", Span::call_site());
219        let crate_ident = crate_ident_new();
220
221        let outer_before = self
222            .captures
223            .iter()
224            .map(|c| c.outer_before_tokens(&crate_ident));
225        let inner_before = self
226            .captures
227            .iter()
228            .map(|c| c.inner_before_tokens(&crate_ident));
229        let outer_after = self
230            .captures
231            .iter()
232            .map(|c| c.outer_after_tokens(&crate_ident, &closure_ident));
233
234        let arg_values = self.args.iter().enumerate().map(|(index, arg)| {
235            let err_msg = format!("Wrong type for argument {index}: {{:?}}");
236            quote! {
237                let #arg = ::core::result::Result::unwrap_or_else(
238                    #crate_ident::Value::get(&#values_ident[#index]),
239                    |e| panic!(#err_msg, e),
240                );
241            }
242        });
243        let arg_names = &self.args;
244        let args_len = self.args.len();
245        let closure = &self.closure;
246        let constructor = Ident::new(self.constructor, Span::call_site());
247
248        let deprecated = if self.constructor == "new" {
249            quote! {
250                {
251                    #[deprecated = "Using old-style closure! syntax"]
252                    macro_rules! closure { () => {}; }
253                    closure!();
254                }
255            }
256        } else {
257            quote! {
258                {
259                    #[deprecated = "Using old-style closure_local! syntax"]
260                    macro_rules! closure_local { () => {}; }
261                    closure_local!();
262                }
263            }
264        };
265
266        tokens.extend(quote! {
267            {
268                let #closure_ident = {
269                    #deprecated
270                    #(#outer_before)*
271                    #crate_ident::closure::RustClosure::#constructor(move |#values_ident| {
272                        assert_eq!(
273                            #values_ident.len(),
274                            #args_len,
275                            "Expected {} arguments but got {}",
276                            #args_len,
277                            #values_ident.len(),
278                        );
279                        #(#inner_before)*
280                        #(#arg_values)*
281                        #crate_ident::closure::IntoClosureReturnValue::into_closure_return_value(
282                            (#closure)(#(#arg_names),*)
283                        )
284                    })
285                };
286                #(#outer_after)*
287                #closure_ident
288            }
289        });
290    }
291}
292
293pub(crate) fn closure_inner(
294    input: proc_macro::TokenStream,
295    constructor: &'static str,
296) -> proc_macro::TokenStream {
297    let mut closure = syn::parse_macro_input!(input as Closure);
298    closure.constructor = constructor;
299    closure.into_token_stream().into()
300}