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}