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