cynthia_macros/
lib.rs

1#![forbid(unsafe_code, future_incompatible, rust_2018_idioms)]
2#![deny(missing_debug_implementations, nonstandard_style)]
3#![recursion_limit = "512"]
4
5use proc_macro::TokenStream;
6use quote::{quote, quote_spanned};
7use syn::spanned::Spanned;
8
9#[cfg(not(test))]
10#[proc_macro_attribute]
11pub fn main(_attr: TokenStream, item: TokenStream) -> TokenStream {
12    let input = syn::parse_macro_input!(item as syn::ItemFn);
13    let ret = &input.sig.output;
14    let inputs = &input.sig.inputs;
15    let name = &input.sig.ident;
16    let body = &input.block;
17    let attrs = &input.attrs;
18    let vis = &input.vis;
19
20    if name != "main" {
21        return TokenStream::from(quote_spanned! { name.span() =>
22            compile_error!("only the main function can be tagged with #[cynthia::main]"),
23        });
24    }
25
26    if input.sig.asyncness.is_none() {
27        return TokenStream::from(quote_spanned! { input.span() =>
28            compile_error!("the async keyword is missing from the function declaration"),
29        });
30    }
31
32    let result = quote! {
33        #vis fn main() #ret {
34            #(#attrs)*
35            async fn main(#inputs) #ret {
36                #body
37            }
38
39            cynthia::runtime::block_on(async {
40                main().await
41            })
42        }
43
44    };
45
46    result.into()
47}
48
49#[proc_macro_attribute]
50pub fn test(_attr: TokenStream, item: TokenStream) -> TokenStream {
51    let input = syn::parse_macro_input!(item as syn::ItemFn);
52    let ret = &input.sig.output;
53    let name = &input.sig.ident;
54    let body = &input.block;
55    let attrs = &input.attrs;
56    let vis = &input.vis;
57
58    if input.sig.asyncness.is_none() {
59        return TokenStream::from(quote_spanned! { input.span() =>
60            compile_error!("the async keyword is missing from the function declaration"),
61        });
62    }
63
64    let result = quote! {
65        #[::core::prelude::v1::test]
66        #(#attrs)*
67        #vis fn #name() #ret {
68            cynthia::runtime::block_on(async { #body })
69        }
70    };
71
72    result.into()
73}
74
75#[proc_macro_attribute]
76pub fn bench(_attr: TokenStream, item: TokenStream) -> TokenStream {
77    let input = syn::parse_macro_input!(item as syn::ItemFn);
78    let ret = &input.sig.output;
79    let args = &input.sig.inputs;
80    let name = &input.sig.ident;
81    let body = &input.block;
82    let attrs = &input.attrs;
83    let vis = &input.vis;
84
85    if input.sig.asyncness.is_none() {
86        return TokenStream::from(quote_spanned! { input.span() =>
87            compile_error!("the async keyword is missing from the function declaration"),
88        });
89    }
90
91    if !args.is_empty() {
92        return TokenStream::from(quote_spanned! { args.span() =>
93            compile_error!("async benchmarks don't take any arguments"),
94        });
95    }
96
97    let result = quote! {
98        #[::core::prelude::v1::bench]
99        #(#attrs)*
100        #vis fn #name(b: &mut test::Bencher) #ret {
101            cynthia::runtime::block_on(async {
102                #body
103            })
104        }
105    };
106
107    result.into()
108}