rustolio_core_macros/
lib.rs1use proc_macro::TokenStream;
12use quote::quote;
13use syn::{ItemFn, Visibility, parse_macro_input};
14
15const EXPECTED_ENTRY_SIGNATURE: &str = r#"
16The application entry should have the following signature:
17
18#[rustolio::entry]
19pub async fn main() {
20 // ...
21}
22"#;
23
24#[proc_macro_attribute]
25pub fn entry(_attr: TokenStream, item: TokenStream) -> TokenStream {
26 let input_fn = parse_macro_input!(item as ItemFn);
27
28 if let Err(e) = check(&input_fn) {
29 return e;
30 }
31
32 let web_entry = quote! {
33 #[cfg(target_arch = "wasm32")]
34 #[doc(hidden)]
35 mod wasm_entry {
36 use super::*;
37 use __core_macros::wasm_bindgen;
38 use __core_macros::wasm_bindgen::prelude::*;
39 use __core_macros::wasm_bindgen_futures;
40
41 #[cfg(target_arch = "wasm32")]
43 #[wasm_bindgen(js_name = entry)]
44 #input_fn
45 }
46 };
47
48 let server_main = quote! {
49 #[cfg(not(target_arch = "wasm32"))]
50 #[doc(hidden)]
51 mod non_wasm_main {
52 use super::*;
53 use __core_macros::tokio;
54
55 #[tokio::main]
56 #[allow(unused)]
57 #input_fn
58 }
59 #[cfg(not(target_arch = "wasm32"))]
60 pub use non_wasm_main::main;
61 };
62
63 let expanded = quote! {
64 #web_entry
65 #server_main
66 };
67
68 expanded.into()
69}
70
71fn check(input_fn: &ItemFn) -> Result<(), TokenStream> {
72 if !matches!(input_fn.vis, Visibility::Public(_)) {
73 return Err(syn::Error::new_spanned(
74 input_fn.sig.clone(),
75 format!(
76 "Expected function to be public: {}",
77 EXPECTED_ENTRY_SIGNATURE
78 ),
79 )
80 .to_compile_error()
81 .into());
82 };
83
84 if input_fn.sig.asyncness.is_none() {
85 return Err(syn::Error::new_spanned(
86 input_fn.sig.clone(),
87 format!(
88 "Expected function to be async: {}",
89 EXPECTED_ENTRY_SIGNATURE
90 ),
91 )
92 .to_compile_error()
93 .into());
94 };
95
96 if input_fn.sig.ident != "main" {
97 return Err(syn::Error::new_spanned(
98 input_fn.sig.clone(),
99 format!(
100 "Expected function to be named `main`: {}",
101 EXPECTED_ENTRY_SIGNATURE
102 ),
103 )
104 .to_compile_error()
105 .into());
106 }
107
108 if !input_fn.sig.generics.params.is_empty() {
109 return Err(syn::Error::new_spanned(
110 input_fn.sig.clone(),
111 format!(
112 "Expected function to have no generics: {}",
113 EXPECTED_ENTRY_SIGNATURE
114 ),
115 )
116 .to_compile_error()
117 .into());
118 }
119
120 Ok(())
121}
122
123