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 `{alias}`. If you don't want to panic, use `#[upgrade_or]`, `#[upgrade_or_else]` or `#[upgrade_or_default]`",
270 );
271 quote! {
272 let Some(#alias) = #crate_ident::clone::Upgrade::upgrade(&#alias) else {
273 panic!(#err_msg);
274 };
275 }
276 }
277 UpgradeBehaviour::Default
278 | UpgradeBehaviour::Expression(_)
279 | UpgradeBehaviour::Closure(_) => {
280 let err_msg = format!("Failed to upgrade `{alias}`");
281 quote! {
282 let Some(#alias) = #crate_ident::clone::Upgrade::upgrade(&#alias) else {
283 #crate_ident::g_debug!(
284 #crate_ident::CLONE_MACRO_LOG_DOMAIN,
285 #err_msg,
286 );
287 return (#upgrade_failure_closure_ident)();
288 };
289 }
290 }
291 UpgradeBehaviour::Unit => {
292 let err_msg = format!("Failed to upgrade `{alias}`");
293 let unit_return = unit_return.unwrap_or_else(|| {
294 quote! { return; }
295 });
296 quote! {
297 let Some(#alias) = #crate_ident::clone::Upgrade::upgrade(&#alias) else {
298 #crate_ident::g_debug!(
299 #crate_ident::CLONE_MACRO_LOG_DOMAIN,
300 #err_msg,
301 );
302 #unit_return
303 };
304 }
305 }
306 },
307 CaptureKind::WeakAllowNone => quote! {
308 let #alias = #crate_ident::clone::Upgrade::upgrade(&#alias);
309 },
310 _ => Default::default(),
311 }
312 }
313}
314
315#[derive(Clone)]
316enum ClosureOrAsync {
317 Closure(ExprClosure),
318 Async(ExprAsync),
319}
320
321impl Parse for ClosureOrAsync {
322 fn parse(input: ParseStream) -> syn::Result<Self> {
323 let expr = input.parse::<Expr>()?;
324 match expr {
325 Expr::Async(async_) => {
326 if async_.capture.is_none() {
327 return Err(syn::Error::new_spanned(
328 async_,
329 "async blocks need to capture variables by move. Please add the `move` keyword",
330 ));
331 }
332
333 Ok(ClosureOrAsync::Async(async_))
334 }
335 Expr::Closure(closure) => {
336 if closure.capture.is_none() {
337 return Err(syn::Error::new_spanned(
338 closure,
339 "closures need to capture variables by move. Please add the `move` keyword",
340 ));
341 }
342
343 Ok(ClosureOrAsync::Closure(closure))
344 }
345 _ => Err(syn::Error::new_spanned(
346 expr,
347 "only closures and async blocks are supported",
348 )),
349 }
350 }
351}
352
353impl ToTokens for ClosureOrAsync {
354 fn to_tokens(&self, tokens: &mut TokenStream) {
355 match self {
356 ClosureOrAsync::Closure(ref c) => c.to_tokens(tokens),
357 ClosureOrAsync::Async(ref a) => a.to_tokens(tokens),
358 }
359 }
360}
361
362struct Clone {
363 captures: Vec<Capture>,
364 upgrade_behaviour: UpgradeBehaviour,
365 body: ClosureOrAsync,
366}
367
368impl Parse for Clone {
369 fn parse(input: ParseStream) -> syn::Result<Self> {
370 if input.is_empty() {
371 return Err(syn::Error::new(
372 Span::call_site(),
373 "expected a closure or async block",
374 ));
375 }
376
377 let mut captures: Vec<Capture> = vec![];
378 let mut upgrade_behaviour: Option<(UpgradeBehaviour, Span)> = None;
379
380 loop {
381 let attrs = input.call(Attribute::parse_outer)?;
391 if attrs.is_empty() {
392 break;
393 };
394
395 if let Some(capture) = Capture::maybe_parse(&attrs, input)? {
396 if capture.kind == CaptureKind::Watch {
397 return Err(syn::Error::new_spanned(
398 &attrs[0],
399 "watch variable captures are not supported",
400 ));
401 }
402
403 captures.push(capture);
404 } else if let Some(behaviour) = UpgradeBehaviour::maybe_parse(&attrs, input)? {
405 if upgrade_behaviour.is_some() {
406 return Err(syn::Error::new_spanned(
407 &attrs[0],
408 "multiple upgrade failure attributes are not supported",
409 ));
410 }
411
412 upgrade_behaviour = Some((behaviour, attrs[0].span()));
413 break;
414 } else if let Some(ident) = attrs[0].path().get_ident() {
415 return Err(syn::Error::new_spanned(
416 &attrs[0],
417 format!(
418 "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",
419 ),
420 ));
421 } else {
422 return Err(syn::Error::new_spanned(
423 &attrs[0],
424 "unsupported attribute: only `strong`, `weak`, `weak_allow_none`, `to_owned`, `upgrade_or_else`, `upgrade_or_default` and `upgrade_or_panic` are supported",
425 ));
426 }
427 }
428
429 if let Some((_, ref span)) = upgrade_behaviour {
430 if captures.iter().all(|c| c.kind != CaptureKind::Weak) {
431 return Err(syn::Error::new(
432 *span,
433 "upgrade failure attribute can only be used together with weak variable captures",
434 ));
435 }
436 }
437
438 let upgrade_behaviour = upgrade_behaviour.map(|x| x.0).unwrap_or_default();
439
440 let body = input.parse::<ClosureOrAsync>()?;
442
443 if input.peek(Token![,]) {
445 input.parse::<Token![,]>()?;
446 }
447
448 Ok(Clone {
449 captures,
450 upgrade_behaviour,
451 body,
452 })
453 }
454}
455
456impl ToTokens for Clone {
457 fn to_tokens(&self, tokens: &mut TokenStream) {
458 let crate_ident = crate_ident_new();
459
460 let upgrade_failure_closure_ident =
461 Ident::new("____upgrade_failure_closure", Span::call_site());
462
463 let outer_before = self
464 .captures
465 .iter()
466 .map(|c| c.outer_before_tokens(&crate_ident));
467 let inner_before = self.captures.iter().map(|c| {
468 c.inner_before_tokens(
469 &crate_ident,
470 &self.upgrade_behaviour,
471 &upgrade_failure_closure_ident,
472 None,
473 )
474 });
475
476 let upgrade_failure_closure = match self.upgrade_behaviour {
477 UpgradeBehaviour::Default => Some(quote! {
478 let #upgrade_failure_closure_ident = ::std::default::Default::default;
479 }),
480 UpgradeBehaviour::Expression(ref expr) => Some(quote! {
481 let #upgrade_failure_closure_ident = move || {
482 #expr
483 };
484 }),
485 UpgradeBehaviour::Closure(ref closure) => Some(quote! {
486 let #upgrade_failure_closure_ident = #closure;
487 }),
488 _ => None,
489 };
490
491 let body = match self.body {
492 ClosureOrAsync::Closure(ref c) => {
493 let ExprClosure {
494 attrs,
495 lifetimes,
496 constness,
497 movability,
498 asyncness,
499 capture,
500 or1_token,
501 inputs,
502 or2_token,
503 output,
504 body,
505 } = c;
506
507 quote! {
508 #(#attrs)*
509 #lifetimes
510 #constness
511 #movability
512 #asyncness
513 #capture
514 #or1_token
515 #inputs
516 #or2_token
517 #output
518 {
519 #upgrade_failure_closure
520 #(#inner_before)*
521 #body
522 }
523 }
524 }
525 ClosureOrAsync::Async(ref a) => {
526 let ExprAsync {
527 attrs,
528 async_token,
529 capture,
530 block,
531 } = a;
532
533 let stmts = &block.stmts;
537
538 quote! {
539 #(#attrs)*
540 #async_token
541 #capture
542 {
543 #upgrade_failure_closure
544 #(#inner_before)*
545 #(#stmts)*
546 }
547 }
548 }
549 };
550
551 tokens.extend(quote! {
552 {
553 #(#outer_before)*
554 #body
555 }
556 });
557 }
558}
559
560pub(crate) fn clone_inner(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
561 let clone = syn::parse_macro_input!(input as Clone);
562 clone.into_token_stream().into()
563}