item_impl.rs 2.3 KB

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