1extern crate proc_macro;
5
6use proc_macro::TokenStream;
7use proc_macro2::TokenStream as Tokens;
8
9use quote::quote;
10
11use syn::AttributeArgs;
12use syn::ItemFn;
13use syn::Meta;
14use syn::NestedMeta;
15use syn::parse_macro_input;
16use syn::parse_quote;
17use syn::Path;
18use syn::ReturnType;
19
20
21#[deprecated(
74 since = "0.2.8",
75 note = "test-env-log has been renamed to test-log; use it instead"
76)]
77#[proc_macro_attribute]
78pub fn test(attr: TokenStream, item: TokenStream) -> TokenStream {
79 let args = parse_macro_input!(attr as AttributeArgs);
80 let input = parse_macro_input!(item as ItemFn);
81
82 let inner_test = match args.as_slice() {
83 [] => parse_quote! { ::core::prelude::v1::test },
84 [NestedMeta::Meta(Meta::Path(path))] => path.clone(),
85 _ => panic!("unsupported attributes supplied: {}", quote! { args }),
86 };
87
88 expand_wrapper(&inner_test, &input)
89}
90
91
92fn expand_logging_init() -> Tokens {
94 #[cfg(feature = "log")]
95 quote! {
96 {
97 let _ = ::env_logger::builder().is_test(true).try_init();
98 }
99 }
100 #[cfg(not(feature = "log"))]
101 quote! {}
102}
103
104
105fn expand_tracing_init() -> Tokens {
107 #[cfg(feature = "trace")]
108 quote! {
109 {
110 let __internal_event_filter = {
111 use ::tracing_subscriber::fmt::format::FmtSpan;
112
113 match ::std::env::var("RUST_LOG_SPAN_EVENTS") {
114 Ok(value) => {
115 value
116 .to_ascii_lowercase()
117 .split(",")
118 .map(|filter| match filter.trim() {
119 "new" => FmtSpan::NEW,
120 "enter" => FmtSpan::ENTER,
121 "exit" => FmtSpan::EXIT,
122 "close" => FmtSpan::CLOSE,
123 "active" => FmtSpan::ACTIVE,
124 "full" => FmtSpan::FULL,
125 _ => panic!("test-env-log: RUST_LOG_SPAN_EVENTS must contain filters separated by `,`.\n\t\
126 For example: `active` or `new,close`\n\t\
127 Supported filters: new, enter, exit, close, active, full\n\t\
128 Got: {}", value),
129 })
130 .fold(FmtSpan::NONE, |acc, filter| filter | acc)
131 },
132 Err(::std::env::VarError::NotUnicode(_)) =>
133 panic!("test-env-log: RUST_LOG_SPAN_EVENTS must contain a valid UTF-8 string"),
134 Err(::std::env::VarError::NotPresent) => FmtSpan::NONE,
135 }
136 };
137
138 let subscriber = ::tracing_subscriber::FmtSubscriber::builder()
139 .with_env_filter(::tracing_subscriber::EnvFilter::from_default_env())
140 .with_span_events(__internal_event_filter)
141 .with_test_writer()
142 .finish();
143 let _ = ::tracing::subscriber::set_global_default(subscriber);
144 }
145 }
146 #[cfg(not(feature = "trace"))]
147 quote! {}
148}
149
150
151fn expand_wrapper(inner_test: &Path, wrappee: &ItemFn) -> TokenStream {
153 let attrs = &wrappee.attrs;
154 let async_ = &wrappee.sig.asyncness;
155 let await_ = if async_.is_some() {
156 quote! {.await}
157 } else {
158 quote! {}
159 };
160 let body = &wrappee.block;
161 let test_name = &wrappee.sig.ident;
162
163 let ret = match &wrappee.sig.output {
166 ReturnType::Default => quote! {},
167 ReturnType::Type(_, type_) => quote! {-> #type_},
168 };
169
170 let logging_init = expand_logging_init();
171 let tracing_init = expand_tracing_init();
172
173 let result = quote! {
174 #[#inner_test]
175 #(#attrs)*
176 #async_ fn #test_name() #ret {
177 #async_ fn test_impl() #ret {
178 #body
179 }
180
181 #logging_init
182 #tracing_init
183
184 test_impl()#await_
185 }
186 };
187 result.into()
188}