clickhouse_testing_macros/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{ItemFn, parse_macro_input};
4
5#[proc_macro_attribute]
6pub fn test(_args: TokenStream, input: TokenStream) -> TokenStream {
7    let func = parse_macro_input!(input as ItemFn);
8    let func_name = &func.sig.ident;
9    let func_name_str = func_name.to_string();
10    let func_vis = &func.vis;
11    let func_attrs = &func.attrs;
12    let func_inputs = &func.sig.inputs;
13    let func_output = &func.sig.output;
14    let func_block = &func.block;
15
16    let inner_name = syn::Ident::new(&format!("__{}_inner", func_name), func_name.span());
17
18    let handle_result = match func_output {
19        syn::ReturnType::Type(_, ty) if matches!(ty.as_ref(), syn::Type::Path(type_path) if type_path.path.segments.last().is_some_and(|seg| seg.ident == "Result")) =>
20        {
21            quote! {
22                match test_result {
23                    Ok(val) => {
24                        if let Err(e) = clickhouse_testing::cleanup_test(&client).await {
25                            panic!("Failed to cleanup test {:?} data: {:?}", #func_name_str, e);
26                        }
27                        val
28                    }
29                    Err(e) => panic!("Test {:?} failed: {:?}", #func_name_str, e),
30                }
31            }
32        }
33        _ => quote! {
34            let val = test_result;
35            if let Err(e) = clickhouse_testing::cleanup_test(&client).await {
36                panic!("Failed to cleanup test {:?} data: {:?}", #func_name_str, e);
37            }
38            val
39        },
40    };
41
42    let expanded = quote! {
43        #(#func_attrs)*
44        #[tokio::test]
45        #func_vis async fn #func_name() {
46            let module_path = module_path!();
47            let client = match clickhouse_testing::init_test(module_path, #func_name_str).await {
48                Ok(c) => c,
49                Err(e) => panic!("Failed to setup test {:?} client: {:?}", #func_name_str, e),
50            };
51
52            let test_result = #inner_name(client.clone()).await;
53            #handle_result
54        }
55
56        async fn #inner_name(#func_inputs) #func_output #func_block
57    };
58
59    expanded.into()
60}