kayrx_macro/
lib.rs

1//! language-level attributes for Kayrx.
2//!
3//! # Examples
4//!
5//! ```
6//! #[kayrx::main]
7//! async fn main() {
8//!     println!("Hello, world!");
9//! }
10//! ```
11//!
12
13#![recursion_limit = "512"]
14
15extern crate proc_macro;
16
17use proc_macro::TokenStream;
18use quote::{quote, quote_spanned};
19use syn::spanned::Spanned;
20
21/// Enables an async main function.
22///
23/// # Examples
24///
25/// ```ignore
26/// #[kayrx::main]
27/// async fn main() -> std::io::Result<()> {
28///     Ok(())
29/// }
30/// ```
31#[cfg(not(test))] // NOTE: exporting main breaks tests,  Work around for rust-lang/rust#62127
32#[proc_macro_attribute]
33pub fn main(_attr: TokenStream, item: TokenStream) -> TokenStream {
34    let input = syn::parse_macro_input!(item as syn::ItemFn);
35
36    let ret = &input.sig.output;
37    let inputs = &input.sig.inputs;
38    let name = &input.sig.ident;
39    let body = &input.block;
40    let attrs = &input.attrs;
41
42    if name != "main" {
43        return TokenStream::from(quote_spanned! { name.span() =>
44            compile_error!("only the main function can be tagged with #[kayrx::main]"),
45        });
46    }
47
48    if input.sig.asyncness.is_none() {
49        return TokenStream::from(quote_spanned! { input.span() =>
50            compile_error!("the async keyword is missing from the function declaration"),
51        });
52    }
53
54    let result = quote! {
55        fn main() #ret {
56            #(#attrs)*
57            async fn main(#inputs) #ret {
58                #body
59            }
60
61            kayrx::karx::exec(async {
62                main().await
63            })
64        }
65
66    };
67
68    result.into()
69}
70
71/// Enables an async test function.
72///
73/// # Examples
74///
75/// ```ignore
76/// #[kayrx::test]
77/// async fn my_test() -> std::io::Result<()> {
78///     assert_eq!(2 * 2, 4);
79///     Ok(())
80/// }
81/// ```
82#[proc_macro_attribute]
83pub fn test(_attr: TokenStream, item: TokenStream) -> TokenStream {
84    let input = syn::parse_macro_input!(item as syn::ItemFn);
85
86    let ret = &input.sig.output;
87    let name = &input.sig.ident;
88    let body = &input.block;
89    let attrs = &input.attrs;
90
91    if input.sig.asyncness.is_none() {
92        return TokenStream::from(quote_spanned! { input.span() =>
93            compile_error!("the async keyword is missing from the function declaration"),
94        });
95    }
96
97    let result = quote! {
98        #[test]
99        #(#attrs)*
100        fn #name() #ret {
101            kayrx::karx::exec(async { #body })
102        }
103    };
104
105    result.into()
106}
107
108
109/// Enables an futures async main function.
110///
111/// # Examples
112///
113/// ```ignore
114/// #[kayrx::futures]
115/// async fn main() -> std::io::Result<()> {
116///     Ok(())
117/// }
118/// ```
119#[proc_macro_attribute]
120pub fn futures(_attr: TokenStream, item: TokenStream) -> TokenStream {
121    let input = syn::parse_macro_input!(item as syn::ItemFn);
122
123    let ret = &input.sig.output;
124    let inputs = &input.sig.inputs;
125    let name = &input.sig.ident;
126    let body = &input.block;
127    let attrs = &input.attrs;
128
129    if name != "main" {
130        return TokenStream::from(quote_spanned! { name.span() =>
131            compile_error!("only the main function can be tagged with #[kayrx::futures]"),
132        });
133    }
134
135    if input.sig.asyncness.is_none() {
136        return TokenStream::from(quote_spanned! { input.span() =>
137            compile_error!("the futures async keyword is missing from the function declaration"),
138        });
139    }
140
141    let result = quote! {
142        fn main() #ret {
143            #(#attrs)*
144            async fn main(#inputs) #ret {
145                #body
146            }
147
148            kayrx::karx::futures::default().exec(async {
149                main().await
150            })
151        }
152
153    };
154
155    result.into()
156}