1mod macro_args;
2
3use macro_args::TraitAndImpls;
4use proc_macro2::TokenStream;
5use proc_macro_error::{emit_call_site_error, proc_macro_error};
6use quote::format_ident;
7use quote::quote;
8use syn::ItemFn;
9
10fn repeat_function_with_mappings(func: &ItemFn, trait_and_impls: TraitAndImpls) -> TokenStream {
35 let impl_blocks: Vec<TokenStream> = trait_and_impls
36 .structs
37 .iter()
38 .map(|struc| {
39 let fn_ident = format_ident!("impl_{}", struc.ident);
40 let trait_ident = &trait_and_impls.base_trait.ident;
41 let trait_generics = &trait_and_impls.base_trait.generics;
42
43 let struct_ident = &struc.ident;
44 let struct_generics = &struc.generics;
45 let stmts = &func.block.stmts;
46
47 quote! {
48 fn #fn_ident() {
49 type #trait_ident#trait_generics = #struct_ident#struct_generics;
50 #(#stmts)*
51 }
52
53 #fn_ident();
54 }
55 })
56 .collect();
57
58 let attrs = &func.attrs;
59 let vis = &func.vis;
60 let sig = &func.sig;
61
62 quote! {
63 #(#attrs)*
64 #[allow(non_snake_case)]
65 #vis #sig
66 {
67 #(#impl_blocks)*
68 }
69 }
70}
71
72#[proc_macro_attribute]
77#[proc_macro_error]
78pub fn test_impl(
79 args: proc_macro::TokenStream,
80 input: proc_macro::TokenStream,
81) -> proc_macro::TokenStream {
82 let args = match syn::parse::<TraitAndImpls>(args) {
83 Ok(a) => a,
84 Err(e) => {
85 emit_call_site_error!("Could not parse macro arguments: {}", e);
86 return proc_macro::TokenStream::new();
87 }
88 };
89
90 let fn_def = match syn::parse::<ItemFn>(input) {
91 Ok(f) => f,
92 Err(e) => {
93 emit_call_site_error!("You must use this macro with a function: {}", e);
94 return proc_macro::TokenStream::new();
95 }
96 };
97
98 let impl_checks = args.output_impl_checks(&fn_def.sig.ident);
99 let mapped = repeat_function_with_mappings(&fn_def, args);
100 (quote! {
101 #impl_checks
102 #mapped
103 })
104 .into()
105}