lib.rs

 1use std::mem;
 2
 3use proc_macro::TokenStream;
 4use quote::{format_ident, quote};
 5use syn::{parse_macro_input, parse_quote, AttributeArgs, ItemFn, Meta, NestedMeta};
 6
 7#[proc_macro_attribute]
 8pub fn test(args: TokenStream, function: TokenStream) -> TokenStream {
 9    let mut namespace = format_ident!("gpui");
10
11    let args = syn::parse_macro_input!(args as AttributeArgs);
12    for arg in args {
13        match arg {
14            NestedMeta::Meta(Meta::Path(name))
15                if name.get_ident().map_or(false, |n| n == "self") =>
16            {
17                namespace = format_ident!("crate");
18            }
19            other => {
20                return TokenStream::from(
21                    syn::Error::new_spanned(other, "invalid argument").into_compile_error(),
22                )
23            }
24        }
25    }
26
27    let mut inner_fn = parse_macro_input!(function as ItemFn);
28    let inner_fn_attributes = mem::take(&mut inner_fn.attrs);
29    let inner_fn_name = format_ident!("_{}", inner_fn.sig.ident);
30    let outer_fn_name = mem::replace(&mut inner_fn.sig.ident, inner_fn_name.clone());
31    let mut outer_fn: ItemFn = if inner_fn.sig.asyncness.is_some() {
32        parse_quote! {
33            #[test]
34            fn #outer_fn_name() {
35                #inner_fn
36
37                #namespace::App::test_async((), move |cx| async {
38                    #inner_fn_name(cx).await;
39                });
40            }
41        }
42    } else {
43        parse_quote! {
44            #[test]
45            fn #outer_fn_name() {
46                #inner_fn
47
48                #namespace::App::test((), |cx| {
49                    #inner_fn_name(cx);
50                });
51            }
52        }
53    };
54    outer_fn.attrs.extend(inner_fn_attributes);
55
56    TokenStream::from(quote!(#outer_fn))
57}