Skip to main content

facet_testhelpers_macros/
lib.rs

1use unsynn::*;
2
3keyword! {
4    KFn = "fn";
5}
6
7unsynn! {
8    struct UntilFn {
9        items: Any<Cons<Except<KFn>, TokenTree>>,
10    }
11
12    struct UntilBody {
13        items: Any<Cons<Except<BraceGroup>, TokenTree>>,
14    }
15
16    struct Body {
17        items: BraceGroup,
18    }
19
20    struct FunctionDecl {
21        until_fn: UntilFn, _fn: KFn, name: Ident,
22        until_body: UntilBody, body: Body
23    }
24}
25
26impl quote::ToTokens for UntilFn {
27    fn to_tokens(&self, tokens: &mut unsynn::TokenStream) {
28        self.items.to_tokens(tokens)
29    }
30}
31
32impl quote::ToTokens for UntilBody {
33    fn to_tokens(&self, tokens: &mut unsynn::TokenStream) {
34        self.items.to_tokens(tokens)
35    }
36}
37
38impl quote::ToTokens for Body {
39    fn to_tokens(&self, tokens: &mut unsynn::TokenStream) {
40        tokens.extend(self.items.0.stream())
41    }
42}
43
44/// Test attribute macro that sets up tracing before running the test.
45///
46/// # Usage
47///
48/// Basic usage (uses `#[test]`):
49/// ```ignore
50/// #[facet_testhelpers::test]
51/// fn my_test() {
52///     // tracing is set up automatically
53/// }
54/// ```
55///
56/// With a custom test attribute (e.g., for async tests):
57/// ```ignore
58/// #[facet_testhelpers::test(tokio::test)]
59/// async fn my_async_test() {
60///     // tracing is set up automatically
61/// }
62/// ```
63#[proc_macro_attribute]
64pub fn test(
65    attr: proc_macro::TokenStream,
66    item: proc_macro::TokenStream,
67) -> proc_macro::TokenStream {
68    let item = TokenStream::from(item);
69    let mut i = item.to_token_iter();
70    let fdecl = i.parse::<FunctionDecl>().unwrap();
71
72    let FunctionDecl {
73        until_fn,
74        _fn,
75        name,
76        until_body,
77        body,
78    } = fdecl;
79
80    // If an attribute argument is provided, use it as the test attribute
81    // e.g., #[facet_testhelpers::test(tokio::test)] -> #[tokio::test]
82    let test_attr = if attr.is_empty() {
83        quote::quote! { #[::core::prelude::rust_2024::test] }
84    } else {
85        let attr = TokenStream::from(attr);
86        quote::quote! { #[#attr] }
87    };
88
89    quote::quote! {
90        #test_attr
91        #until_fn fn #name #until_body {
92            ::facet_testhelpers::setup();
93
94            #body
95        }
96    }
97    .into()
98}