mod macro_args;
use macro_args::TraitAndImpls;
use proc_macro2::TokenStream;
use proc_macro_error::{emit_call_site_error, proc_macro_error};
use quote::format_ident;
use quote::quote;
use syn::ItemFn;
fn repeat_function_with_mappings(func: &ItemFn, trait_and_impls: TraitAndImpls) -> TokenStream {
let impl_blocks: Vec<TokenStream> = trait_and_impls
.structs
.iter()
.map(|struc| {
let fn_ident = format_ident!("impl_{}", struc.ident);
let trait_ident = &trait_and_impls.base_trait.ident;
let trait_generics = &trait_and_impls.base_trait.generics;
let struct_ident = &struc.ident;
let struct_generics = &struc.generics;
let stmts = &func.block.stmts;
quote! {
fn #fn_ident() {
type #trait_ident#trait_generics = #struct_ident#struct_generics;
#(#stmts)*
}
#fn_ident();
}
})
.collect();
let attrs = &func.attrs;
let vis = &func.vis;
let sig = &func.sig;
quote! {
#(#attrs)*
#[allow(non_snake_case)]
#vis #sig
{
#(#impl_blocks)*
}
}
}
#[proc_macro_attribute]
#[proc_macro_error]
pub fn test_impl(
args: proc_macro::TokenStream,
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let args = match syn::parse::<TraitAndImpls>(args) {
Ok(a) => a,
Err(e) => {
emit_call_site_error!("Could not parse macro arguments: {}", e);
return proc_macro::TokenStream::new();
}
};
let fn_def = match syn::parse::<ItemFn>(input) {
Ok(f) => f,
Err(e) => {
emit_call_site_error!("You must use this macro with a function: {}", e);
return proc_macro::TokenStream::new();
}
};
let impl_checks = args.output_impl_checks(&fn_def.sig.ident);
let mapped = repeat_function_with_mappings(&fn_def, args);
(quote! {
#impl_checks
#mapped
})
.into()
}