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