| 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, TokenStreamExt}; |
| 5 | use syn::{ext::IdentExt, spanned::Spanned, Token}; |
| 6 | |
| 7 | use crate::utils::crate_ident_new; |
| 8 | |
| 9 | #[derive (Clone, Copy, Debug, Eq, PartialEq)] |
| 10 | enum CaptureKind { |
| 11 | Watch, |
| 12 | WeakAllowNone, |
| 13 | Strong, |
| 14 | ToOwned, |
| 15 | } |
| 16 | |
| 17 | struct Capture { |
| 18 | name: TokenStream, |
| 19 | alias: Option<syn::Ident>, |
| 20 | kind: CaptureKind, |
| 21 | start: Span, |
| 22 | } |
| 23 | |
| 24 | impl Capture { |
| 25 | fn alias(&self) -> TokenStream { |
| 26 | if let Some(ref a) = self.alias { |
| 27 | a.to_token_stream() |
| 28 | } else { |
| 29 | self.name.to_token_stream() |
| 30 | } |
| 31 | } |
| 32 | fn outer_before_tokens(&self, crate_ident: &TokenStream) -> TokenStream { |
| 33 | let alias = self.alias(); |
| 34 | let name = &self.name; |
| 35 | match self.kind { |
| 36 | CaptureKind::Watch => quote! { |
| 37 | let #alias = #crate_ident::object::Watchable::watched_object(&#name); |
| 38 | }, |
| 39 | CaptureKind::WeakAllowNone => quote! { |
| 40 | let #alias = #crate_ident::clone::Downgrade::downgrade(&#name); |
| 41 | }, |
| 42 | CaptureKind::Strong => quote! { |
| 43 | let #alias = #name.clone(); |
| 44 | }, |
| 45 | CaptureKind::ToOwned => quote! { |
| 46 | let #alias = ::std::borrow::ToOwned::to_owned(&*#name); |
| 47 | }, |
| 48 | } |
| 49 | } |
| 50 | |
| 51 | fn outer_after_tokens(&self, crate_ident: &TokenStream, closure_ident: &Ident) -> TokenStream { |
| 52 | let name = &self.name; |
| 53 | match self.kind { |
| 54 | CaptureKind::Watch => quote! { |
| 55 | #crate_ident::object::Watchable::watch_closure(&#name, &#closure_ident); |
| 56 | }, |
| 57 | _ => Default::default(), |
| 58 | } |
| 59 | } |
| 60 | |
| 61 | fn inner_before_tokens(&self, crate_ident: &TokenStream) -> TokenStream { |
| 62 | let alias = self.alias(); |
| 63 | match self.kind { |
| 64 | CaptureKind::Watch => { |
| 65 | quote! { |
| 66 | let #alias = unsafe { #alias.borrow() }; |
| 67 | let #alias = ::core::convert::AsRef::as_ref(&#alias); |
| 68 | } |
| 69 | } |
| 70 | CaptureKind::WeakAllowNone => quote! { |
| 71 | let #alias = #crate_ident::clone::Upgrade::upgrade(&#alias); |
| 72 | }, |
| 73 | _ => Default::default(), |
| 74 | } |
| 75 | } |
| 76 | } |
| 77 | |
| 78 | impl syn::parse::Parse for CaptureKind { |
| 79 | fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { |
| 80 | input.parse::<Token![@]>()?; |
| 81 | let mut idents = TokenStream::new(); |
| 82 | idents.append(input.call(syn::Ident::parse_any)?); |
| 83 | while input.peek(Token![-]) { |
| 84 | input.parse::<Token![-]>()?; |
| 85 | idents.append(input.call(syn::Ident::parse_any)?); |
| 86 | } |
| 87 | let keyword = idents |
| 88 | .clone() |
| 89 | .into_iter() |
| 90 | .map(|i| i.to_string()) |
| 91 | .collect::<Vec<_>>() |
| 92 | .join("-" ); |
| 93 | match keyword.as_str() { |
| 94 | "strong" => Ok(CaptureKind::Strong), |
| 95 | "watch" => Ok(CaptureKind::Watch), |
| 96 | "weak-allow-none" => Ok(CaptureKind::WeakAllowNone), |
| 97 | "to-owned" => Ok(CaptureKind::ToOwned), |
| 98 | k => Err(syn::Error::new( |
| 99 | idents.span(), |
| 100 | format!("Unknown keyword ` {}`, only `watch`, `weak-allow-none`, `to-owned` and `strong` are allowed" , |
| 101 | k), |
| 102 | )), |
| 103 | } |
| 104 | } |
| 105 | } |
| 106 | |
| 107 | impl syn::parse::Parse for Capture { |
| 108 | fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { |
| 109 | let start = input.span(); |
| 110 | let kind = input.parse()?; |
| 111 | let mut name = TokenStream::new(); |
| 112 | name.append(input.call(syn::Ident::parse_any)?); |
| 113 | while input.peek(Token![.]) { |
| 114 | input.parse::<Token![.]>()?; |
| 115 | name.append(proc_macro2::Punct::new('.' , proc_macro2::Spacing::Alone)); |
| 116 | name.append(input.call(syn::Ident::parse_any)?); |
| 117 | } |
| 118 | let alias = if input.peek(Token![as]) { |
| 119 | input.parse::<Token![as]>()?; |
| 120 | input.parse()? |
| 121 | } else { |
| 122 | None |
| 123 | }; |
| 124 | if alias.is_none() { |
| 125 | if name.to_string() == "self" { |
| 126 | return Err(syn::Error::new_spanned( |
| 127 | name, |
| 128 | "Can't use `self` as variable name. Try storing it in a temporary variable or \ |
| 129 | rename it using `as`." , |
| 130 | )); |
| 131 | } |
| 132 | if name.to_string().contains('.' ) { |
| 133 | return Err(syn::Error::new( |
| 134 | name.span(), |
| 135 | format!( |
| 136 | "` {}`: Field accesses are not allowed as is, you must rename it!" , |
| 137 | name |
| 138 | ), |
| 139 | )); |
| 140 | } |
| 141 | } |
| 142 | Ok(Capture { |
| 143 | name, |
| 144 | alias, |
| 145 | kind, |
| 146 | start, |
| 147 | }) |
| 148 | } |
| 149 | } |
| 150 | |
| 151 | struct Closure { |
| 152 | captures: Vec<Capture>, |
| 153 | args: Vec<Ident>, |
| 154 | closure: syn::ExprClosure, |
| 155 | constructor: &'static str, |
| 156 | } |
| 157 | |
| 158 | impl syn::parse::Parse for Closure { |
| 159 | fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { |
| 160 | let mut captures: Vec<Capture> = vec![]; |
| 161 | if input.peek(Token![@]) { |
| 162 | loop { |
| 163 | let capture = input.parse::<Capture>()?; |
| 164 | if capture.kind == CaptureKind::Watch { |
| 165 | if let Some(existing) = captures.iter().find(|c| c.kind == CaptureKind::Watch) { |
| 166 | return Err(syn::Error::new( |
| 167 | existing.start, |
| 168 | "Only one `@watch` capture is allowed per closure" , |
| 169 | )); |
| 170 | } |
| 171 | } |
| 172 | captures.push(capture); |
| 173 | if input.peek(Token![,]) { |
| 174 | input.parse::<Token![,]>()?; |
| 175 | if !input.peek(Token![@]) { |
| 176 | break; |
| 177 | } |
| 178 | } else { |
| 179 | break; |
| 180 | } |
| 181 | } |
| 182 | } |
| 183 | if !captures.is_empty() { |
| 184 | input.parse::<Token![=>]>()?; |
| 185 | } |
| 186 | let mut closure = input.parse::<syn::ExprClosure>()?; |
| 187 | if closure.asyncness.is_some() { |
| 188 | return Err(syn::Error::new_spanned( |
| 189 | closure, |
| 190 | "Async closure not allowed" , |
| 191 | )); |
| 192 | } |
| 193 | if !captures.is_empty() && closure.capture.is_none() { |
| 194 | return Err(syn::Error::new_spanned( |
| 195 | closure, |
| 196 | "Closure with captures needs to be \"moved \" so please add `move` before closure" , |
| 197 | )); |
| 198 | } |
| 199 | let args = closure |
| 200 | .inputs |
| 201 | .iter() |
| 202 | .enumerate() |
| 203 | .map(|(i, _)| Ident::new(&format!("____value {i}" ), Span::call_site())) |
| 204 | .collect(); |
| 205 | closure.capture = None; |
| 206 | Ok(Closure { |
| 207 | captures, |
| 208 | args, |
| 209 | closure, |
| 210 | constructor: "new" , |
| 211 | }) |
| 212 | } |
| 213 | } |
| 214 | |
| 215 | impl ToTokens for Closure { |
| 216 | fn to_tokens(&self, tokens: &mut TokenStream) { |
| 217 | let closure_ident = Ident::new("____closure" , Span::call_site()); |
| 218 | let values_ident = Ident::new("____values" , Span::call_site()); |
| 219 | let crate_ident = crate_ident_new(); |
| 220 | |
| 221 | let outer_before = self |
| 222 | .captures |
| 223 | .iter() |
| 224 | .map(|c| c.outer_before_tokens(&crate_ident)); |
| 225 | let inner_before = self |
| 226 | .captures |
| 227 | .iter() |
| 228 | .map(|c| c.inner_before_tokens(&crate_ident)); |
| 229 | let outer_after = self |
| 230 | .captures |
| 231 | .iter() |
| 232 | .map(|c| c.outer_after_tokens(&crate_ident, &closure_ident)); |
| 233 | |
| 234 | let arg_values = self.args.iter().enumerate().map(|(index, arg)| { |
| 235 | let err_msg = format!("Wrong type for argument {index}: {{:? }}" ); |
| 236 | quote! { |
| 237 | let #arg = ::core::result::Result::unwrap_or_else( |
| 238 | #crate_ident::Value::get(&#values_ident[#index]), |
| 239 | |e| panic!(#err_msg, e), |
| 240 | ); |
| 241 | } |
| 242 | }); |
| 243 | let arg_names = &self.args; |
| 244 | let args_len = self.args.len(); |
| 245 | let closure = &self.closure; |
| 246 | let constructor = Ident::new(self.constructor, Span::call_site()); |
| 247 | |
| 248 | let deprecated = if self.constructor == "new" { |
| 249 | quote! { |
| 250 | { |
| 251 | #[deprecated = "Using old-style closure! syntax" ] |
| 252 | macro_rules! closure { () => {}; } |
| 253 | closure!(); |
| 254 | } |
| 255 | } |
| 256 | } else { |
| 257 | quote! { |
| 258 | { |
| 259 | #[deprecated = "Using old-style closure_local! syntax" ] |
| 260 | macro_rules! closure_local { () => {}; } |
| 261 | closure_local!(); |
| 262 | } |
| 263 | } |
| 264 | }; |
| 265 | |
| 266 | tokens.extend(quote! { |
| 267 | { |
| 268 | let #closure_ident = { |
| 269 | #deprecated |
| 270 | #(#outer_before)* |
| 271 | #crate_ident::closure::RustClosure::#constructor(move |#values_ident| { |
| 272 | assert_eq!( |
| 273 | #values_ident.len(), |
| 274 | #args_len, |
| 275 | "Expected {} arguments but got {}" , |
| 276 | #args_len, |
| 277 | #values_ident.len(), |
| 278 | ); |
| 279 | #(#inner_before)* |
| 280 | #(#arg_values)* |
| 281 | #crate_ident::closure::IntoClosureReturnValue::into_closure_return_value( |
| 282 | (#closure)(#(#arg_names),*) |
| 283 | ) |
| 284 | }) |
| 285 | }; |
| 286 | #(#outer_after)* |
| 287 | #closure_ident |
| 288 | } |
| 289 | }); |
| 290 | } |
| 291 | } |
| 292 | |
| 293 | pub(crate) fn closure_inner( |
| 294 | input: proc_macro::TokenStream, |
| 295 | constructor: &'static str, |
| 296 | ) -> proc_macro::TokenStream { |
| 297 | let mut closure: Closure = syn::parse_macro_input!(input as Closure); |
| 298 | closure.constructor = constructor; |
| 299 | closure.into_token_stream().into() |
| 300 | } |
| 301 | |