libtest_mimic_collect_macro/
lib.rs1use proc_macro::TokenStream;
2use proc_macro2::{Ident, Span};
3use quote::quote;
4use syn::{
5 parse_macro_input, spanned::Spanned, AngleBracketedGenericArguments, GenericArgument, ItemFn,
6 LitStr, PathArguments, ReturnType, Type, TypePath, TypeTuple,
7};
8
9#[proc_macro_attribute]
12pub fn test(_args: TokenStream, input: TokenStream) -> TokenStream {
13 let ItemFn { sig, block, .. } = parse_macro_input!(input as ItemFn);
14
15 let ident = &sig.ident;
16 let test_name = ident.to_string();
17 let test_name_str = LitStr::new(&test_name, Span::call_site());
18 let ctor_name = format!("__{}_add_test", test_name);
19 let ctor_ident = Ident::new(&ctor_name, Span::call_site());
20
21 let ret_type_unit = quote! { Result<(), ::libtest_mimic_collect::libtest_mimic::Failed> };
22 let ret_type_completion = quote! { Result<::libtest_mimic_collect::libtest_mimic::Completion, ::libtest_mimic_collect::libtest_mimic::Failed> };
23
24 let trial = match &sig.output {
25 ReturnType::Default => {
26 quote! {
27 ::libtest_mimic_collect::libtest_mimic::Trial::test(#test_name_str, || -> #ret_type_unit {
28 #ident();
29 Ok(())
30 })
31 }
32 }
33 ReturnType::Type(_, ty) => {
34 let result_segment = if let Type::Path(TypePath { path, qself: None }) = ty.as_ref() {
35 path.segments
36 .last()
37 .filter(|segment| segment.ident == "Result")
38 } else {
39 None
40 };
41
42 match result_segment {
43 Some(segment) => {
44 let is_unit_result = match &segment.arguments {
45 PathArguments::None => false,
46 PathArguments::AngleBracketed(AngleBracketedGenericArguments {
47 args,
48 ..
49 }) => {
50 matches!(
51 args.first(),
52 Some(GenericArgument::Type(Type::Tuple(TypeTuple { elems, .. }))) if elems.is_empty()
53 )
54 }
55 PathArguments::Parenthesized(args) => {
56 return syn::Error::new(args.span(), "unexpected return type")
57 .to_compile_error()
58 .into();
59 }
60 };
61
62 if is_unit_result {
63 quote! {
64 ::libtest_mimic_collect::libtest_mimic::Trial::test(#test_name_str, || -> #ret_type_unit {
65 Ok(#ident()?.into())
66 })
67 }
68 } else {
69 quote! {
70 ::libtest_mimic_collect::libtest_mimic::Trial::ignorable_test(#test_name_str, || -> #ret_type_completion {
71 Ok(#ident()?.into())
72 })
73 }
74 }
75 }
76 None => {
77 quote! {
78 ::libtest_mimic_collect::libtest_mimic::Trial::test(#test_name_str, || -> #ret_type_unit {
79 ::libtest_mimic_collect::TestCollection::convert_result(#ident())
80 })
81 }
82 }
83 }
84 }
85 };
86
87 (quote! {
88 #sig #block
89
90 #[::libtest_mimic_collect::ctor]
91 fn #ctor_ident() {
92 ::libtest_mimic_collect::TestCollection::add_test(#trial);
93 }
94 })
95 .into()
96}