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}