#![deny(missing_docs)]
extern crate proc_macro;
use proc_macro::TokenStream;
use proc_macro2::TokenStream as Tokens;
use quote::quote;
use syn::parse_macro_input;
use syn::ItemFn;
#[proc_macro_attribute]
pub fn test(attr: TokenStream, item: TokenStream) -> TokenStream {
let inner_test = if attr.is_empty() {
quote! { ::core::prelude::v1::test }
} else {
attr.into()
};
let input = parse_macro_input!(item as ItemFn);
let ItemFn {
attrs,
vis,
sig,
block,
} = input;
let logging_init = expand_logging_init();
let tracing_init = expand_tracing_init();
let result = quote! {
#[#inner_test]
#(#attrs)*
#vis #sig {
mod init {
pub fn init() {
#logging_init
#tracing_init
}
}
init::init();
#block
}
};
result.into()
}
fn expand_logging_init() -> Tokens {
#[cfg(feature = "log")]
quote! {
{
let _ = ::env_logger::builder().is_test(true).try_init();
}
}
#[cfg(not(feature = "log"))]
quote! {}
}
fn expand_tracing_init() -> Tokens {
#[cfg(feature = "trace")]
quote! {
{
let __internal_event_filter = {
use ::tracing_subscriber::fmt::format::FmtSpan;
match ::std::env::var_os("RUST_LOG_SPAN_EVENTS") {
Some(mut value) => {
value.make_ascii_lowercase();
let value = value.to_str().expect("test-log: RUST_LOG_SPAN_EVENTS must be valid UTF-8");
value
.split(",")
.map(|filter| match filter.trim() {
"new" => FmtSpan::NEW,
"enter" => FmtSpan::ENTER,
"exit" => FmtSpan::EXIT,
"close" => FmtSpan::CLOSE,
"active" => FmtSpan::ACTIVE,
"full" => FmtSpan::FULL,
_ => panic!("test-log: RUST_LOG_SPAN_EVENTS must contain filters separated by `,`.\n\t\
For example: `active` or `new,close`\n\t\
Supported filters: new, enter, exit, close, active, full\n\t\
Got: {}", value),
})
.fold(FmtSpan::NONE, |acc, filter| filter | acc)
},
None => FmtSpan::NONE,
}
};
let _ = ::tracing_subscriber::FmtSubscriber::builder()
.with_env_filter(::tracing_subscriber::EnvFilter::from_default_env())
.with_span_events(__internal_event_filter)
.with_test_writer()
.try_init();
}
}
#[cfg(not(feature = "trace"))]
quote! {}
}