1extern crate proc_macro;
5
6use proc_macro::TokenStream;
7use proc_macro2::TokenStream as Tokens;
8
9use quote::quote;
10use syn::{parse_macro_input, punctuated::Punctuated, FnArg, ItemFn, ReturnType, Token};
11
12fn expand_tracing_init() -> Tokens {
18 quote! {
19 {
20 let trace_level = std::env::var("LANCE_TRACING");
21 if let Ok(trace_level) = trace_level {
22
23 let level_filter = match trace_level.as_str() {
24 "debug" => ::tracing_subscriber::filter::LevelFilter::DEBUG,
25 "info" => ::tracing_subscriber::filter::LevelFilter::INFO,
26 "warn" => ::tracing_subscriber::filter::LevelFilter::WARN,
27 "error" => ::tracing_subscriber::filter::LevelFilter::ERROR,
28 "trace" => ::tracing_subscriber::filter::LevelFilter::TRACE,
29 _ => panic!("Unexpected trace level {}", trace_level),
30 };
31
32 let (chrome_layer, _guard) = tracing_chrome::ChromeLayerBuilder::new().trace_style(tracing_chrome::TraceStyle::Async).include_args(true).build();
33 let subscriber = ::tracing_subscriber::registry::Registry::default();
34 let chrome_layer = ::tracing_subscriber::Layer::with_filter(chrome_layer, level_filter);
35 let subscriber = tracing_subscriber::prelude::__tracing_subscriber_SubscriberExt::with(
36 subscriber, chrome_layer);
37 let sub_guard = ::tracing::subscriber::set_default(subscriber);
38 Some((_guard, sub_guard))
39 } else {
40 None
41 }
42 }
43 }
44}
45
46fn extract_args(inputs: &Punctuated<FnArg, Token![,]>) -> Punctuated<Tokens, Token![,]> {
47 inputs
48 .iter()
49 .map(|arg| match arg {
50 FnArg::Receiver(receiver) => {
51 let slf = receiver.self_token;
52 quote! { #slf }
53 }
54 FnArg::Typed(typed) => {
55 let pat = &typed.pat;
56 quote! { #pat }
57 }
58 })
59 .collect()
60}
61
62fn expand_wrapper(wrapped_attr: Tokens, wrappee: ItemFn) -> Tokens {
65 let attrs = &wrappee.attrs;
66 let async_ = &wrappee.sig.asyncness;
67 let await_ = if async_.is_some() {
68 quote! {.await}
69 } else {
70 quote! {}
71 };
72 let body = &wrappee.block;
73 let test_name = &wrappee.sig.ident;
74 let inputs = &wrappee.sig.inputs;
75 let args = extract_args(inputs);
76
77 let ret = match &wrappee.sig.output {
80 ReturnType::Default => quote! {},
81 ReturnType::Type(_, type_) => quote! {-> #type_},
82 };
83
84 let tracing_init = expand_tracing_init();
85
86 let result = quote! {
88 #[#wrapped_attr]
89 #(#attrs)*
90 #async_ fn #test_name(#inputs) #ret {
91 #async_ fn test_impl(#inputs) #ret {
92 #body
93 }
94
95 mod init {
96 pub fn init() -> Option<(tracing_chrome::FlushGuard, tracing::subscriber::DefaultGuard)> {
97 #tracing_init
98 }
99 }
100
101 let _guard = init::init();
102 test_impl(#args)#await_
103 }
104 };
105 result
106}
107
108#[proc_macro_attribute]
141pub fn test(args: TokenStream, input: TokenStream) -> TokenStream {
142 let input = parse_macro_input!(input as ItemFn);
143
144 expand_wrapper(args.into(), input).into()
145}