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 let mut captures: Vec<Capture> = vec![];
372 let mut upgrade_behaviour: Option<(UpgradeBehaviour, Span)> = None;
373
374 loop {
375 let attrs = input.call(Attribute::parse_outer)?;
385 if attrs.is_empty() {
386 break;
387 };
388
389 if let Some(capture) = Capture::maybe_parse(&attrs, input)? {
390 if capture.kind == CaptureKind::Watch {
391 return Err(syn::Error::new_spanned(
392 &attrs[0],
393 "watch variable captures are not supported",
394 ));
395 }
396
397 captures.push(capture);
398 } else if let Some(behaviour) = UpgradeBehaviour::maybe_parse(&attrs, input)? {
399 if upgrade_behaviour.is_some() {
400 return Err(syn::Error::new_spanned(
401 &attrs[0],
402 "multiple upgrade failure attributes are not supported",
403 ));
404 }
405
406 upgrade_behaviour = Some((behaviour, attrs[0].span()));
407 break;
408 } else if let Some(ident) = attrs[0].path().get_ident() {
409 return Err(syn::Error::new_spanned(
410 &attrs[0],
411 format!(
412 "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",
413 ),
414 ));
415 } else {
416 return Err(syn::Error::new_spanned(
417 &attrs[0],
418 "unsupported attribute: only `strong`, `weak`, `weak_allow_none`, `to_owned`, `upgrade_or_else`, `upgrade_or_default` and `upgrade_or_panic` are supported",
419 ));
420 }
421 }
422
423 if let Some((_, ref span)) = upgrade_behaviour {
424 if captures.iter().all(|c| c.kind != CaptureKind::Weak) {
425 return Err(syn::Error::new(
426 *span,
427 "upgrade failure attribute can only be used together with weak variable captures",
428 ));
429 }
430 }
431
432 let upgrade_behaviour = upgrade_behaviour.map(|x| x.0).unwrap_or_default();
433
434 let body = input.parse::<ClosureOrAsync>()?;
436
437 if input.peek(Token![,]) {
439 input.parse::<Token![,]>()?;
440 }
441
442 Ok(Clone {
443 captures,
444 upgrade_behaviour,
445 body,
446 })
447 }
448}
449
450impl ToTokens for Clone {
451 fn to_tokens(&self, tokens: &mut TokenStream) {
452 let crate_ident = crate_ident_new();
453
454 let upgrade_failure_closure_ident =
455 Ident::new("____upgrade_failure_closure", Span::call_site());
456
457 let outer_before = self
458 .captures
459 .iter()
460 .map(|c| c.outer_before_tokens(&crate_ident));
461 let inner_before = self.captures.iter().map(|c| {
462 c.inner_before_tokens(
463 &crate_ident,
464 &self.upgrade_behaviour,
465 &upgrade_failure_closure_ident,
466 None,
467 )
468 });
469
470 let upgrade_failure_closure = match self.upgrade_behaviour {
471 UpgradeBehaviour::Default => Some(quote! {
472 let #upgrade_failure_closure_ident = ::std::default::Default::default;
473 }),
474 UpgradeBehaviour::Expression(ref expr) => Some(quote! {
475 let #upgrade_failure_closure_ident = move || {
476 #expr
477 };
478 }),
479 UpgradeBehaviour::Closure(ref closure) => Some(quote! {
480 let #upgrade_failure_closure_ident = #closure;
481 }),
482 _ => None,
483 };
484
485 let body = match self.body {
486 ClosureOrAsync::Closure(ref c) => {
487 let ExprClosure {
488 attrs,
489 lifetimes,
490 constness,
491 movability,
492 asyncness,
493 capture,
494 or1_token,
495 inputs,
496 or2_token,
497 output,
498 body,
499 } = c;
500
501 quote! {
502 #(#attrs)*
503 #lifetimes
504 #constness
505 #movability
506 #asyncness
507 #capture
508 #or1_token
509 #inputs
510 #or2_token
511 #output
512 {
513 #upgrade_failure_closure
514 #(#inner_before)*
515 #body
516 }
517 }
518 }
519 ClosureOrAsync::Async(ref a) => {
520 let ExprAsync {
521 attrs,
522 async_token,
523 capture,
524 block,
525 } = a;
526
527 let stmts = &block.stmts;
531
532 quote! {
533 #(#attrs)*
534 #async_token
535 #capture
536 {
537 #upgrade_failure_closure
538 #(#inner_before)*
539 #(#stmts)*
540 }
541 }
542 }
543 };
544
545 tokens.extend(quote! {
546 {
547 #(#outer_before)*
548 #body
549 }
550 });
551 }
552}
553
554pub(crate) fn clone_inner(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
555 let clone = syn::parse_macro_input!(input as Clone);
556 clone.into_token_stream().into()
557}