e/proc-macros/impl-for-refs/core/src/lib.rs
2024-05-04 10:00:44 -04:00

109 lines
1.8 KiB
Rust

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::<TokenStream>();
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))
}
}
},
)
}
}