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