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