tesults_test_macros/
lib.rs1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{parse_macro_input, ItemFn};
4
5#[proc_macro_attribute]
10pub fn test(_attr: TokenStream, item: TokenStream) -> TokenStream {
11 let input = parse_macro_input!(item as ItemFn);
12 let fn_name = &input.sig.ident;
13 let fn_name_str = fn_name.to_string();
14 let fn_body = &input.block;
15 let attrs = &input.attrs;
16 let vis = &input.vis;
17
18 let should_panic = attrs.iter().any(|a| a.path().is_ident("should_panic"));
19
20 let passed_expr = if should_panic {
23 quote! { __tt_result.is_err() }
24 } else {
25 quote! { __tt_result.is_ok() }
26 };
27
28 let reason_expr = if should_panic {
31 quote! {
32 if __tt_result.is_ok() {
33 ::std::string::String::from("Test was expected to panic but did not")
34 } else {
35 ::std::string::String::new()
36 }
37 }
38 } else {
39 quote! {
40 match __tt_result {
41 ::std::result::Result::Err(ref __tt_err) => {
42 if let ::std::option::Option::Some(s) =
43 __tt_err.downcast_ref::<::std::string::String>()
44 {
45 s.clone()
46 } else if let ::std::option::Option::Some(s) =
47 __tt_err.downcast_ref::<&str>()
48 {
49 s.to_string()
50 } else {
51 ::std::string::String::from("Test panicked")
52 }
53 }
54 _ => ::std::string::String::new(),
55 }
56 }
57 };
58
59 let expanded = quote! {
60 #[test]
61 #(#attrs)*
62 #vis fn #fn_name() {
63 let __tt_start = ::tesults_test::__private::now_ms();
64 let __tt_result = ::std::panic::catch_unwind(
65 ::std::panic::AssertUnwindSafe(|| { #fn_body })
66 );
67 let __tt_end = ::tesults_test::__private::now_ms();
68 let __tt_passed = #passed_expr;
69 let __tt_reason = #reason_expr;
70 ::tesults_test::__private::record_result(
71 #fn_name_str,
72 module_path!(),
73 __tt_passed,
74 __tt_reason,
75 __tt_start,
76 __tt_end,
77 );
78 if let ::std::result::Result::Err(__tt_err) = __tt_result {
79 ::std::panic::resume_unwind(__tt_err);
80 }
81 }
82 };
83
84 expanded.into()
85}