1use proc_macro::TokenStream;
8use quote::quote;
9use syn::{ItemFn, parse_macro_input};
10
11#[proc_macro_attribute]
28pub fn cotis_start(_attr: TokenStream, item: TokenStream) -> TokenStream {
29 let input_fn = parse_macro_input!(item as ItemFn);
30
31 if !input_fn.sig.inputs.is_empty() {
32 return syn::Error::new_spanned(
33 &input_fn.sig,
34 "#[cotis_start] functions must not take parameters",
35 )
36 .to_compile_error()
37 .into();
38 }
39
40 if input_fn.sig.asyncness.is_some() {
41 return syn::Error::new_spanned(
42 &input_fn.sig,
43 "#[cotis_start] cannot be applied to async functions. Use #[cotis_start_async] instead.",
44 )
45 .to_compile_error()
46 .into();
47 }
48
49 let fn_name = &input_fn.sig.ident;
50
51 let expanded = quote! {
52 #input_fn
53
54 #[doc(hidden)]
55 #[unsafe(no_mangle)]
56 pub extern "Rust" fn __cotis_launch_hook() {
57 #fn_name();
58 }
59 };
60
61 expanded.into()
62}
63
64#[proc_macro_attribute]
81pub fn cotis_start_async(_attr: TokenStream, item: TokenStream) -> TokenStream {
82 let input_fn = parse_macro_input!(item as ItemFn);
83
84 if !input_fn.sig.inputs.is_empty() {
85 return syn::Error::new_spanned(
86 &input_fn.sig,
87 "#[cotis_start_async] functions must not take parameters",
88 )
89 .to_compile_error()
90 .into();
91 }
92
93 let fn_name = &input_fn.sig.ident;
94 let hook_body = if input_fn.sig.asyncness.is_some() {
95 quote! {
96 Box::new(#fn_name())
97 }
98 } else {
99 quote! {
100 Box::new(async move {
101 #fn_name();
102 })
103 }
104 };
105
106 let expanded = quote! {
107 #input_fn
108
109 #[doc(hidden)]
110 #[unsafe(no_mangle)]
111 pub extern "Rust" fn __cotis_launch_async_hook() -> Box<dyn ::core::future::Future<Output = ()>> {
112 #hook_body
113 }
114 };
115
116 expanded.into()
117}