1 | // Take a look at the license at the top of the repository in the LICENSE file. |
2 | |
3 | use proc_macro2::{Ident, Span, TokenStream}; |
4 | use quote::{quote, ToTokens, TokenStreamExt}; |
5 | use syn::{ext::IdentExt, spanned::Spanned, Token}; |
6 | |
7 | use crate::utils::crate_ident_new; |
8 | |
9 | #[derive (Clone, Copy, Debug, Eq, PartialEq)] |
10 | enum CaptureKind { |
11 | Watch, |
12 | WeakAllowNone, |
13 | Strong, |
14 | ToOwned, |
15 | } |
16 | |
17 | struct Capture { |
18 | name: TokenStream, |
19 | alias: Option<syn::Ident>, |
20 | kind: CaptureKind, |
21 | start: Span, |
22 | } |
23 | |
24 | impl 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 | |
78 | impl 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 | |
107 | impl 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 | |
151 | struct Closure { |
152 | captures: Vec<Capture>, |
153 | args: Vec<Ident>, |
154 | closure: syn::ExprClosure, |
155 | constructor: &'static str, |
156 | } |
157 | |
158 | impl 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 | |
215 | impl 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 | |
293 | pub(crate) fn closure_inner( |
294 | input: proc_macro::TokenStream, |
295 | constructor: &'static str, |
296 | ) -> proc_macro::TokenStream { |
297 | let mut closure: Closure = syn::parse_macro_input!(input as Closure); |
298 | closure.constructor = constructor; |
299 | closure.into_token_stream().into() |
300 | } |
301 | |