use proc_macro2::TokenStream; use quote::quote; use syn::{FnArg, ItemTrait, Pat}; pub fn impl_for_refs_core( _attrs: TokenStream, item: TokenStream, ) -> TokenStream { let trt: ItemTrait = match syn::parse2(item.clone()) { Ok(e) => e, Err(e) => return e.into_compile_error(), }; let trt_name = &trt.ident; let fns = trt .items .iter() .filter_map(|i| match i { syn::TraitItem::Fn(f) => { if f.default.is_some() { return None; } let sig = &f.sig; let fn_name = &f.sig.ident; let args = sig.inputs.iter().map(|i| match i { FnArg::Receiver(_s) => { quote!((&**self)) }, FnArg::Typed(t) => match t.pat.as_ref() { Pat::Ident(e) => quote!(#e), _ => todo!(), }, }); Some(quote! { #sig { #trt_name::#fn_name(#(#args),*) } }) }, _ => None, }) .collect::(); quote! { #item impl<'a, T> #trt_name for &'a T where T: ?Sized + #trt_name { #fns } impl<'a, T> #trt_name for &'a mut T where T: ?Sized + #trt_name { #fns } } } #[cfg(test)] mod test { use proc_macro2::TokenStream; use quote::quote; use crate::impl_for_refs_core; fn expect_output( attrs: TokenStream, input: TokenStream, output: TokenStream, ) { assert_eq!( impl_for_refs_core(attrs, input).to_string(), output.to_string() ) } #[test] fn test_works() { expect_output( quote!(), quote! { #[impl_for_refs] trait Test { fn test(&self) -> usize; } }, quote! { #[impl_for_refs] trait Test { fn test(&self) -> usize; } impl<'a, T> Test for &'a T where T: ?Sized + Test { fn test(&self) -> usize { Test::test((&**self)) } } impl<'a, T> Test for &'a mut T where T: ?Sized + Test { fn test(&self) -> usize { Test::test((&**self)) } } }, ) } }