1use 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}