1use proc_macro2::{Ident, Span, TokenStream};
4use quote::{quote, ToTokens};
5use syn::{
6 parse::{Parse, ParseStream},
7 spanned::Spanned,
8 Attribute, Expr, ExprAsync, ExprClosure, Token,
9};
10
11use crate::utils::crate_ident_new;
12
13#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14pub(crate) enum CaptureKind {
15 Watch,
16 Weak,
17 WeakAllowNone,
18 Strong,
19 ToOwned,
20}
21
22impl TryFrom<&'_ Ident> for CaptureKind {
23 type Error = syn::Error;
24
25 fn try_from(s: &Ident) -> Result<Self, Self::Error> {
26 Ok(match s.to_string().as_str() {
27 "watch" => CaptureKind::Watch,
28 "strong" => CaptureKind::Strong,
29 "weak" => CaptureKind::Weak,
30 "weak_allow_none" => CaptureKind::WeakAllowNone,
31 "to_owned" => CaptureKind::ToOwned,
32 _ => {
33 return Err(syn::Error::new(
38 s.span(),
39 format!("unknown capture type `{s}`"),
40 ));
41 }
42 })
43 }
44}
45
46#[derive(Default)]
47pub(crate) enum UpgradeBehaviour {
48 #[default]
49 Unit,
50 Panic,
51 Default,
52 Expression(Expr),
53 Closure(ExprClosure),
54}
55
56impl UpgradeBehaviour {
57 pub(crate) fn maybe_parse(
58 attrs: &[Attribute],
59 input: ParseStream,
60 ) -> syn::Result<Option<Self>> {
61 let attr = &attrs[0];
63 attr.meta.require_path_only()?;
64
65 let Some(attr_name) = attr.path().get_ident() else {
66 return Ok(None);
67 };
68
69 let upgrade_behaviour = match attr_name.to_string().as_str() {
70 "upgrade_or" => {
71 let expr = input.parse::<Expr>()?;
72 input.parse::<Token![,]>()?;
73 UpgradeBehaviour::Expression(expr)
74 }
75 "upgrade_or_else" => {
76 let closure = input.parse::<ExprClosure>()?;
77 if closure.asyncness.is_some() {
78 return Err(syn::Error::new_spanned(
79 &closure,
80 "`upgrade_or_else` closure needs to be a non-async closure",
81 ));
82 }
83 if !closure.inputs.is_empty() {
84 return Err(syn::Error::new_spanned(
85 &closure,
86 "`upgrade_or_else` closure must not have any parameters",
87 ));
88 }
89
90 input.parse::<Token![,]>()?;
91 UpgradeBehaviour::Closure(closure)
92 }
93 "upgrade_or_default" => UpgradeBehaviour::Default,
94 "upgrade_or_panic" => UpgradeBehaviour::Panic,
95 _ => {
96 return Ok(None);
97 }
98 };
99
100 if attrs.len() > 1 {
101 return Err(syn::Error::new_spanned(
102 &attrs[1],
103 format!(
104 "upgrade failure attribute must not be followed by any other attributes. Found {} more attribute{}",
105 attrs.len() - 1,
106 if attrs.len() > 2 { "s" } else { "" },
107 )));
108 }
109
110 let next_attrs = &input.call(Attribute::parse_outer)?;
111 if !next_attrs.is_empty() {
112 return Err(syn::Error::new_spanned(
113 &next_attrs[0],
114 format!(
115 "upgrade failure attribute must not be followed by any other attributes. Found {} more attribute{}",
116 next_attrs.len(),
117 if next_attrs.len() > 1 { "s" } else { "" },
118 )
119 ));
120 }
121
122 Ok(Some(upgrade_behaviour))
123 }
124}
125
126pub(crate) struct Capture {
127 pub(crate) name: Expr,
128 pub(crate) alias: Option<Ident>,
129 pub(crate) kind: CaptureKind,
130}
131
132impl Capture {
133 pub(crate) fn maybe_parse(
134 attrs: &[Attribute],
135 input: ParseStream,
136 ) -> syn::Result<Option<Self>> {
137 let attr = &attrs[0];
139
140 let Some(attr_name) = attr.path().get_ident() else {
141 return Ok(None);
142 };
143 let Ok(kind) = CaptureKind::try_from(attr_name) else {
144 return Ok(None);
145 };
146
147 if attrs.len() > 1 {
148 return Err(syn::Error::new_spanned(
149 &attrs[1],
150 "variable capture attributes must be followed by an identifier",
151 ));
152 }
153
154 let mut alias = None;
155 if let syn::Meta::List(ref list) = attr.meta {
156 list.parse_nested_meta(|meta| {
157 if meta.path.is_ident("rename_to") {
158 let value = meta.value()?;
159 let id = value.parse::<Ident>()?;
160 if alias.is_some() {
161 return Err(meta.error("multiple `rename_to` properties are not allowed"));
162 }
163 alias = Some(id);
164 } else if let Some(ident) = meta.path.get_ident() {
165 return Err(
166 meta.error(
167 format!(
168 "unsupported capture attribute property `{ident}`: only `rename_to` is supported"
169 ),
170 ),
171 );
172 } else {
173 return Err(meta.error("unsupported capture attribute property"));
174 }
175 Ok(())
176 })?;
177 }
178
179 let name = input.parse::<Expr>()?;
180 match name {
181 Expr::Path(ref p) if p.path.get_ident().is_some() => {
182 if p.path.get_ident().unwrap() == "self" && alias.is_none() {
183 return Err(
184 syn::Error::new_spanned(
185 attr,
186 "capture attribute for `self` requires usage of the `rename_to` attribute property",
187 ),
188 );
189 }
190 }
192 _ if alias.is_some() => {
193 }
195 _ => {
196 return Err(
197 syn::Error::new_spanned(
198 attr,
199 "capture attribute for an expression requires usage of the `rename_to` attribute property",
200 ),
201 );
202 }
203 }
204
205 input.parse::<Token![,]>()?;
206
207 Ok(Some(Capture { name, alias, kind }))
208 }
209
210 pub(crate) fn alias(&self) -> TokenStream {
211 if let Some(ref alias) = self.alias {
212 alias.to_token_stream()
213 } else {
214 self.name.to_token_stream()
215 }
216 }
217
218 pub(crate) fn outer_before_tokens(&self, crate_ident: &TokenStream) -> TokenStream {
219 let alias = self.alias();
220 let name = &self.name;
221 match self.kind {
222 CaptureKind::Watch => quote! {
223 let #alias = #crate_ident::object::Watchable::watched_object(&#name);
224 },
225 CaptureKind::Weak | CaptureKind::WeakAllowNone => quote! {
226 let #alias = #crate_ident::clone::Downgrade::downgrade(&#name);
227 },
228 CaptureKind::Strong => quote! {
229 let #alias = #name.clone();
230 },
231 CaptureKind::ToOwned => quote! {
232 let #alias = ::std::borrow::ToOwned::to_owned(&*#name);
233 },
234 }
235 }
236
237 pub(crate) fn outer_after_tokens(
238 &self,
239 crate_ident: &TokenStream,
240 closure_ident: &Ident,
241 ) -> TokenStream {
242 let name = &self.name;
243 match self.kind {
244 CaptureKind::Watch => quote! {
245 #crate_ident::object::Watchable::watch_closure(&#name, &#closure_ident);
246 },
247 _ => Default::default(),
248 }
249 }
250
251 pub(crate) fn inner_before_tokens(
252 &self,
253 crate_ident: &TokenStream,
254 weak_upgrade_failure_kind: &UpgradeBehaviour,
255 upgrade_failure_closure_ident: &Ident,
256 unit_return: Option<TokenStream>,
257 ) -> TokenStream {
258 let alias = self.alias();
259 match self.kind {
260 CaptureKind::Watch => {
261 quote! {
262 let #alias = unsafe { #alias.borrow() };
263 let #alias = ::core::convert::AsRef::as_ref(&#alias);
264 }
265 }
266 CaptureKind::Weak => match weak_upgrade_failure_kind {
267 UpgradeBehaviour::Panic => {
268 let err_msg = format!(
269 "Failed to upgrade `{}`. If you don't want to panic, use `#[upgrade_or]`, `#[upgrade_or_else]` or `#[upgrade_or_default]`",
270 alias,
271 );
272 quote! {
273 let Some(#alias) = #crate_ident::clone::Upgrade::upgrade(&#alias) else {
274 panic!(#err_msg);
275 };
276 }
277 }
278 UpgradeBehaviour::Default
279 | UpgradeBehaviour::Expression(_)
280 | UpgradeBehaviour::Closure(_) => {
281 let err_msg = format!("Failed to upgrade `{}`", alias);
282 quote! {
283 let Some(#alias) = #crate_ident::clone::Upgrade::upgrade(&#alias) else {
284 #crate_ident::g_debug!(
285 #crate_ident::CLONE_MACRO_LOG_DOMAIN,
286 #err_msg,
287 );
288 return (#upgrade_failure_closure_ident)();
289 };
290 }
291 }
292 UpgradeBehaviour::Unit => {
293 let err_msg = format!("Failed to upgrade `{}`", alias);
294 let unit_return = unit_return.unwrap_or_else(|| {
295 quote! { return; }
296 });
297 quote! {
298 let Some(#alias) = #crate_ident::clone::Upgrade::upgrade(&#alias) else {
299 #crate_ident::g_debug!(
300 #crate_ident::CLONE_MACRO_LOG_DOMAIN,
301 #err_msg,
302 );
303 #unit_return
304 };
305 }
306 }
307 },
308 CaptureKind::WeakAllowNone => quote! {
309 let #alias = #crate_ident::clone::Upgrade::upgrade(&#alias);
310 },
311 _ => Default::default(),
312 }
313 }
314}
315
316#[derive(Clone)]
317enum ClosureOrAsync {
318 Closure(ExprClosure),
319 Async(ExprAsync),
320}
321
322impl Parse for ClosureOrAsync {
323 fn parse(input: ParseStream) -> syn::Result<Self> {
324 let expr = input.parse::<Expr>()?;
325 match expr {
326 Expr::Async(async_) => {
327 if async_.capture.is_none() {
328 return Err(syn::Error::new_spanned(
329 async_,
330 "async blocks need to capture variables by move. Please add the `move` keyword",
331 ));
332 }
333
334 Ok(ClosureOrAsync::Async(async_))
335 }
336 Expr::Closure(closure) => {
337 if closure.capture.is_none() {
338 return Err(syn::Error::new_spanned(
339 closure,
340 "closures need to capture variables by move. Please add the `move` keyword",
341 ));
342 }
343
344 Ok(ClosureOrAsync::Closure(closure))
345 }
346 _ => Err(syn::Error::new_spanned(
347 expr,
348 "only closures and async blocks are supported",
349 )),
350 }
351 }
352}
353
354impl ToTokens for ClosureOrAsync {
355 fn to_tokens(&self, tokens: &mut TokenStream) {
356 match self {
357 ClosureOrAsync::Closure(ref c) => c.to_tokens(tokens),
358 ClosureOrAsync::Async(ref a) => a.to_tokens(tokens),
359 }
360 }
361}
362
363struct Clone {
364 captures: Vec<Capture>,
365 upgrade_behaviour: UpgradeBehaviour,
366 body: ClosureOrAsync,
367}
368
369impl Parse for Clone {
370 fn parse(input: ParseStream) -> syn::Result<Self> {
371 if input.is_empty() {
372 return Err(syn::Error::new(
373 Span::call_site(),
374 "expected a closure or async block",
375 ));
376 }
377
378 let mut captures: Vec<Capture> = vec![];
379 let mut upgrade_behaviour: Option<(UpgradeBehaviour, Span)> = None;
380
381 loop {
382 let attrs = input.call(Attribute::parse_outer)?;
392 if attrs.is_empty() {
393 break;
394 };
395
396 if let Some(capture) = Capture::maybe_parse(&attrs, input)? {
397 if capture.kind == CaptureKind::Watch {
398 return Err(syn::Error::new_spanned(
399 &attrs[0],
400 "watch variable captures are not supported",
401 ));
402 }
403
404 captures.push(capture);
405 } else if let Some(behaviour) = UpgradeBehaviour::maybe_parse(&attrs, input)? {
406 if upgrade_behaviour.is_some() {
407 return Err(syn::Error::new_spanned(
408 &attrs[0],
409 "multiple upgrade failure attributes are not supported",
410 ));
411 }
412
413 upgrade_behaviour = Some((behaviour, attrs[0].span()));
414 break;
415 } else if let Some(ident) = attrs[0].path().get_ident() {
416 return Err(syn::Error::new_spanned(
417 &attrs[0],
418 format!(
419 "unsupported attribute `{ident}`: only `strong`, `weak`, `weak_allow_none`, `to_owned`, `upgrade_or`, `upgrade_or_else`, `upgrade_or_default` and `upgrade_or_panic` are supported",
420 ),
421 ));
422 } else {
423 return Err(syn::Error::new_spanned(
424 &attrs[0],
425 "unsupported attribute: only `strong`, `weak`, `weak_allow_none`, `to_owned`, `upgrade_or_else`, `upgrade_or_default` and `upgrade_or_panic` are supported",
426 ));
427 }
428 }
429
430 if let Some((_, ref span)) = upgrade_behaviour {
431 if captures.iter().all(|c| c.kind != CaptureKind::Weak) {
432 return Err(syn::Error::new(
433 *span,
434 "upgrade failure attribute can only be used together with weak variable captures",
435 ));
436 }
437 }
438
439 let upgrade_behaviour = upgrade_behaviour.map(|x| x.0).unwrap_or_default();
440
441 let body = input.parse::<ClosureOrAsync>()?;
443
444 if input.peek(Token![,]) {
446 input.parse::<Token![,]>()?;
447 }
448
449 Ok(Clone {
450 captures,
451 upgrade_behaviour,
452 body,
453 })
454 }
455}
456
457impl ToTokens for Clone {
458 fn to_tokens(&self, tokens: &mut TokenStream) {
459 let crate_ident = crate_ident_new();
460
461 let upgrade_failure_closure_ident =
462 Ident::new("____upgrade_failure_closure", Span::call_site());
463
464 let outer_before = self
465 .captures
466 .iter()
467 .map(|c| c.outer_before_tokens(&crate_ident));
468 let inner_before = self.captures.iter().map(|c| {
469 c.inner_before_tokens(
470 &crate_ident,
471 &self.upgrade_behaviour,
472 &upgrade_failure_closure_ident,
473 None,
474 )
475 });
476
477 let upgrade_failure_closure = match self.upgrade_behaviour {
478 UpgradeBehaviour::Default => Some(quote! {
479 let #upgrade_failure_closure_ident = ::std::default::Default::default;
480 }),
481 UpgradeBehaviour::Expression(ref expr) => Some(quote! {
482 let #upgrade_failure_closure_ident = move || {
483 #expr
484 };
485 }),
486 UpgradeBehaviour::Closure(ref closure) => Some(quote! {
487 let #upgrade_failure_closure_ident = #closure;
488 }),
489 _ => None,
490 };
491
492 let body = match self.body {
493 ClosureOrAsync::Closure(ref c) => {
494 let ExprClosure {
495 attrs,
496 lifetimes,
497 constness,
498 movability,
499 asyncness,
500 capture,
501 or1_token,
502 inputs,
503 or2_token,
504 output,
505 body,
506 } = c;
507
508 quote! {
509 #(#attrs)*
510 #lifetimes
511 #constness
512 #movability
513 #asyncness
514 #capture
515 #or1_token
516 #inputs
517 #or2_token
518 #output
519 {
520 #upgrade_failure_closure
521 #(#inner_before)*
522 #body
523 }
524 }
525 }
526 ClosureOrAsync::Async(ref a) => {
527 let ExprAsync {
528 attrs,
529 async_token,
530 capture,
531 block,
532 } = a;
533
534 let stmts = &block.stmts;
538
539 quote! {
540 #(#attrs)*
541 #async_token
542 #capture
543 {
544 #upgrade_failure_closure
545 #(#inner_before)*
546 #(#stmts)*
547 }
548 }
549 }
550 };
551
552 tokens.extend(quote! {
553 {
554 #(#outer_before)*
555 #body
556 }
557 });
558 }
559}
560
561pub(crate) fn clone_inner(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
562 let clone = syn::parse_macro_input!(input as Clone);
563 clone.into_token_stream().into()
564}