| 1 | // Take a look at the license at the top of the repository in the LICENSE file. |
| 2 | |
| 3 | use proc_macro2::{Ident, Span, TokenStream}; |
| 4 | use quote::{quote, ToTokens}; |
| 5 | use syn::{ |
| 6 | parse::{Parse, ParseStream}, |
| 7 | spanned::Spanned, |
| 8 | Attribute, ExprClosure, Token, |
| 9 | }; |
| 10 | |
| 11 | use crate::{ |
| 12 | clone::{Capture, CaptureKind, UpgradeBehaviour}, |
| 13 | utils::crate_ident_new, |
| 14 | }; |
| 15 | |
| 16 | struct Closure { |
| 17 | captures: Vec<Capture>, |
| 18 | args: Vec<Ident>, |
| 19 | upgrade_behaviour: UpgradeBehaviour, |
| 20 | closure: ExprClosure, |
| 21 | constructor: &'static str, |
| 22 | } |
| 23 | |
| 24 | impl 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 | // There must either be one or no attributes here. Multiple attributes are not |
| 31 | // supported. |
| 32 | // |
| 33 | // If this is a capture attribute, it must be followed by an identifier. |
| 34 | // If this is an upgrade failure attribute, it might be followed by a closure. After the |
| 35 | // upgrade failure attribute there must not be any further attributes. |
| 36 | // |
| 37 | // If this is not an attribute then it is a closure, async closure or async block which |
| 38 | // is handled outside the loop |
| 39 | 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 | // Trailing comma, if any |
| 114 | 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 | |
| 128 | impl 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 | |
| 236 | pub(crate) fn closure_inner( |
| 237 | input: proc_macro::TokenStream, |
| 238 | constructor: &'static str, |
| 239 | ) -> proc_macro::TokenStream { |
| 240 | let mut closure: Closure = syn::parse_macro_input!(input as Closure); |
| 241 | closure.constructor = constructor; |
| 242 | closure.into_token_stream().into() |
| 243 | } |
| 244 | |