1 use hashbrown::HashSet; 2 use proc_macro2::TokenStream; 3 use quote::{quote, quote_spanned, ToTokens}; 4 use syn::punctuated::Punctuated; 5 use syn::spanned::Spanned; 6 use syn::Token; 7 use syn::{ 8 AngleBracketedGenericArguments, Binding, GenericArgument, ImplItem, ItemImpl, Path, 9 PathArguments, 10 }; 11 use PathArguments::AngleBracketed; 12 13 use crate::args::Flag; 14 use crate::gen_caster::generate_caster; 15 16 pub fn process(flags: &HashSet<Flag>, input: ItemImpl) -> TokenStream { 17 let ItemImpl { 18 ref self_ty, 19 ref trait_, 20 ref items, 21 .. 22 } = input; 23 24 let generated = match trait_ { 25 None => quote_spanned! { 26 self_ty.span() => compile_error!("#[cast_to] should only be on an impl of a trait"); 27 }, 28 Some(trait_) => match trait_ { 29 (Some(bang), _, _) => quote_spanned! { 30 bang.span() => compile_error!("#[cast_to] is not for !Trait impl"); 31 }, 32 (None, path, _) => { 33 let path = fully_bound_trait(path, items); 34 generate_caster(self_ty, &path, flags.contains(&Flag::Sync)) 35 } 36 }, 37 }; 38 39 quote! { 40 #input 41 #generated 42 } 43 } 44 45 fn fully_bound_trait(path: &Path, items: &[ImplItem]) -> impl ToTokens { 46 let bindings = items 47 .iter() 48 .filter_map(|item| { 49 if let ImplItem::Type(assoc_ty) = item { 50 Some(GenericArgument::Binding(Binding { 51 ident: assoc_ty.ident.to_owned(), 52 eq_token: Default::default(), 53 ty: assoc_ty.ty.to_owned(), 54 })) 55 } else { 56 None 57 } 58 }) 59 .collect::<Punctuated<_, Token![,]>>(); 60 61 let mut path = path.clone(); 62 63 if bindings.is_empty() { 64 return path; 65 } 66 67 if let Some(last) = path.segments.last_mut() { 68 match &mut last.arguments { 69 PathArguments::None => { 70 last.arguments = AngleBracketed(AngleBracketedGenericArguments { 71 colon2_token: None, 72 lt_token: Default::default(), 73 args: bindings, 74 gt_token: Default::default(), 75 }) 76 } 77 AngleBracketed(args) => args.args.extend(bindings), 78 _ => {} 79 } 80 } 81 path 82 } 83