use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, ItemFn};
#[proc_macro_attribute]
pub fn test(_attr: TokenStream, item: TokenStream) -> TokenStream {
let input = parse_macro_input!(item as ItemFn);
let fn_name = &input.sig.ident;
let fn_name_str = fn_name.to_string();
let fn_body = &input.block;
let attrs = &input.attrs;
let vis = &input.vis;
let should_panic = attrs.iter().any(|a| a.path().is_ident("should_panic"));
let passed_expr = if should_panic {
quote! { __tt_result.is_err() }
} else {
quote! { __tt_result.is_ok() }
};
let reason_expr = if should_panic {
quote! {
if __tt_result.is_ok() {
::std::string::String::from("Test was expected to panic but did not")
} else {
::std::string::String::new()
}
}
} else {
quote! {
match __tt_result {
::std::result::Result::Err(ref __tt_err) => {
if let ::std::option::Option::Some(s) =
__tt_err.downcast_ref::<::std::string::String>()
{
s.clone()
} else if let ::std::option::Option::Some(s) =
__tt_err.downcast_ref::<&str>()
{
s.to_string()
} else {
::std::string::String::from("Test panicked")
}
}
_ => ::std::string::String::new(),
}
}
};
let expanded = quote! {
#[test]
#(#attrs)*
#vis fn #fn_name() {
let __tt_start = ::tesults_test::__private::now_ms();
let __tt_result = ::std::panic::catch_unwind(
::std::panic::AssertUnwindSafe(|| { #fn_body })
);
let __tt_end = ::tesults_test::__private::now_ms();
let __tt_passed = #passed_expr;
let __tt_reason = #reason_expr;
::tesults_test::__private::record_result(
#fn_name_str,
module_path!(),
__tt_passed,
__tt_reason,
__tt_start,
__tt_end,
);
if let ::std::result::Result::Err(__tt_err) = __tt_result {
::std::panic::resume_unwind(__tt_err);
}
}
};
expanded.into()
}