1use proc_macro2::{Ident, Span, TokenStream};
4use quote::{quote, ToTokens};
5use syn::{
6 parse::{Parse, ParseStream},
7 spanned::Spanned,
8 Attribute, ExprClosure, Token,
9};
10
11use crate::{
12 clone::{Capture, CaptureKind, UpgradeBehaviour},
13 utils::crate_ident_new,
14};
15
16struct Closure {
17 captures: Vec<Capture>,
18 args: Vec<Ident>,
19 upgrade_behaviour: UpgradeBehaviour,
20 closure: ExprClosure,
21 constructor: &'static str,
22}
23
24impl Parse for Closure {
25 fn parse(input: ParseStream) -> syn::Result<Self> {
26 if input.is_empty() {
27 return Err(syn::Error::new(Span::call_site(), "expected a closure"));
28 }
29
30 let mut captures: Vec<Capture> = vec![];
31 let mut upgrade_behaviour: Option<(UpgradeBehaviour, Span)> = None;
32
33 loop {
34 let attrs = input.call(Attribute::parse_outer)?;
44 if attrs.is_empty() {
45 break;
46 };
47
48 if let Some(capture) = Capture::maybe_parse(&attrs, input)? {
49 if capture.kind == CaptureKind::Watch
50 && captures.iter().any(|c| c.kind == CaptureKind::Watch)
51 {
52 return Err(syn::Error::new_spanned(
53 &attrs[0],
54 "only one `watch` capture is allowed per closure",
55 ));
56 }
57
58 captures.push(capture);
59 } else if let Some(behaviour) = UpgradeBehaviour::maybe_parse(&attrs, input)? {
60 if upgrade_behaviour.is_some() {
61 return Err(syn::Error::new_spanned(
62 &attrs[0],
63 "multiple upgrade failure attributes are not supported",
64 ));
65 }
66
67 upgrade_behaviour = Some((behaviour, attrs[0].span()));
68 break;
69 } else if let Some(ident) = attrs[0].path().get_ident() {
70 return Err(syn::Error::new_spanned(
71 &attrs[0],
72 format!(
73 "unsupported attribute `{ident}`: only `watch`, `strong`, `weak`, `weak_allow_none`, `to_owned`, `upgrade_or`, `upgrade_or_else`, `upgrade_or_default` and `upgrade_or_panic` are supported",
74 ),
75 ));
76 } else {
77 return Err(syn::Error::new_spanned(
78 &attrs[0],
79 "unsupported attribute: only `strong`, `weak`, `weak_allow_none`, `to_owned`, `upgrade_or_else`, `upgrade_or_default` and `upgrade_or_panic` are supported",
80 ));
81 }
82 }
83
84 if let Some((_, ref span)) = upgrade_behaviour {
85 if captures.iter().all(|c| c.kind != CaptureKind::Weak) {
86 return Err(syn::Error::new(
87 *span,
88 "upgrade failure attribute can only be used together with weak variable captures",
89 ));
90 }
91 }
92
93 let upgrade_behaviour = upgrade_behaviour.map(|x| x.0).unwrap_or_default();
94
95 let mut closure = input.parse::<ExprClosure>()?;
96 if closure.asyncness.is_some() {
97 return Err(syn::Error::new_spanned(
98 closure,
99 "async closures not supported",
100 ));
101 }
102 if !captures.is_empty() && closure.capture.is_none() {
103 return Err(syn::Error::new_spanned(
104 closure,
105 "closures need to capture variables by move. Please add the `move` keyword",
106 ));
107 }
108 closure.capture = None;
109
110 let args = closure
111 .inputs
112 .iter()
113 .enumerate()
114 .map(|(i, _)| Ident::new(&format!("____value{i}"), Span::call_site()))
115 .collect();
116
117 if input.peek(Token![,]) {
119 input.parse::<Token![,]>()?;
120 }
121
122 Ok(Closure {
123 captures,
124 args,
125 upgrade_behaviour,
126 closure,
127 constructor: "new",
128 })
129 }
130}
131
132impl ToTokens for Closure {
133 fn to_tokens(&self, tokens: &mut TokenStream) {
134 let crate_ident = crate_ident_new();
135
136 let closure_ident = Ident::new("____closure", Span::call_site());
137 let values_ident = Ident::new("____values", Span::call_site());
138 let upgrade_failure_closure_ident =
139 Ident::new("____upgrade_failure_closure", Span::call_site());
140 let upgrade_failure_closure_wrapped_ident =
141 Ident::new("____upgrade_failure_closure_wrapped", Span::call_site());
142
143 let outer_before = self
144 .captures
145 .iter()
146 .map(|c| c.outer_before_tokens(&crate_ident));
147 let inner_before = self.captures.iter().map(|c| {
148 c.inner_before_tokens(
149 &crate_ident,
150 &self.upgrade_behaviour,
151 &upgrade_failure_closure_wrapped_ident,
152 Some(quote! {
153 return #crate_ident::closure::IntoClosureReturnValue::into_closure_return_value(());
154 }),
155 )
156 });
157 let outer_after = self
158 .captures
159 .iter()
160 .map(|c| c.outer_after_tokens(&crate_ident, &closure_ident));
161
162 let arg_values = self.args.iter().enumerate().map(|(index, arg)| {
163 let err_msg = format!("Wrong type for argument {index}: {{:?}}");
164 quote! {
165 let #arg = ::core::result::Result::unwrap_or_else(
166 #crate_ident::Value::get(&#values_ident[#index]),
167 |e| panic!(#err_msg, e),
168 );
169 }
170 });
171 let arg_names = &self.args;
172 let args_len = self.args.len();
173 let closure = &self.closure;
174 let constructor = Ident::new(self.constructor, Span::call_site());
175
176 let upgrade_failure_closure = match self.upgrade_behaviour {
177 UpgradeBehaviour::Default => Some(quote! {
178 let #upgrade_failure_closure_ident = ::std::default::Default::default;
179 let #upgrade_failure_closure_wrapped_ident = ||
180 #crate_ident::closure::IntoClosureReturnValue::into_closure_return_value(
181 (#upgrade_failure_closure_ident)()
182 );
183 }),
184 UpgradeBehaviour::Expression(ref expr) => Some(quote! {
185 let #upgrade_failure_closure_ident = move || {
186 #expr
187 };
188 let #upgrade_failure_closure_wrapped_ident = ||
189 #crate_ident::closure::IntoClosureReturnValue::into_closure_return_value(
190 (#upgrade_failure_closure_ident)()
191 );
192 }),
193 UpgradeBehaviour::Closure(ref closure_2) => Some(quote! {
194 let #upgrade_failure_closure_ident = #closure_2;
195 let #upgrade_failure_closure_wrapped_ident = ||
196 #crate_ident::closure::IntoClosureReturnValue::into_closure_return_value(
197 (#upgrade_failure_closure_ident)()
198 );
199 }),
200 _ => None,
201 };
202
203 let assert_return_type = upgrade_failure_closure.is_some().then(|| {
204 quote! {
205 fn ____same<T>(_a: &T, _b: impl Fn() -> T) {}
206 ____same(&____res, #upgrade_failure_closure_ident);
207 }
208 });
209
210 tokens.extend(quote! {
211 {
212 let #closure_ident = {
213 #(#outer_before)*
214 #crate_ident::closure::RustClosure::#constructor(move |#values_ident| {
215 assert_eq!(
216 #values_ident.len(),
217 #args_len,
218 "Expected {} arguments but got {}",
219 #args_len,
220 #values_ident.len(),
221 );
222 #upgrade_failure_closure
223 #(#inner_before)*
224 #(#arg_values)*
225 #crate_ident::closure::IntoClosureReturnValue::into_closure_return_value({
226 let ____res = (#closure)(#(#arg_names),*);
227 #assert_return_type
228 ____res
229 })
230 }
231 )
232 };
233 #(#outer_after)*
234 #closure_ident
235 }
236 });
237 }
238}
239
240pub(crate) fn closure_inner(
241 input: proc_macro::TokenStream,
242 constructor: &'static str,
243) -> proc_macro::TokenStream {
244 let mut closure = syn::parse_macro_input!(input as Closure);
245 closure.constructor = constructor;
246 closure.into_token_stream().into()
247}