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 proc_macro_error::abort;
5use quote::{quote, ToTokens, TokenStreamExt};
6use syn::{ext::IdentExt, spanned::Spanned, Token};
7
8use crate::utils::crate_ident_new;
9
10#[derive(Clone, Copy, Debug, Eq, PartialEq)]
11enum CaptureKind {
12 Watch,
13 WeakAllowNone,
14 Strong,
15 ToOwned,
16}
17
18struct Capture {
19 name: TokenStream,
20 alias: Option<syn::Ident>,
21 kind: CaptureKind,
22 start: Span,
23}
24
25impl 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
79impl 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
108impl 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
150struct Closure {
151 captures: Vec<Capture>,
152 args: Vec<Ident>,
153 closure: syn::ExprClosure,
154 constructor: &'static str,
155}
156
157impl 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
212impl 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
271pub(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