use proc_macro::TokenStream;
use quote::quote;
use syn::{ItemFn, parse_macro_input};
#[proc_macro_attribute]
pub fn cotis_start(_attr: TokenStream, item: TokenStream) -> TokenStream {
let input_fn = parse_macro_input!(item as ItemFn);
if !input_fn.sig.inputs.is_empty() {
return syn::Error::new_spanned(
&input_fn.sig,
"#[cotis_start] functions must not take parameters",
)
.to_compile_error()
.into();
}
if input_fn.sig.asyncness.is_some() {
return syn::Error::new_spanned(
&input_fn.sig,
"#[cotis_start] cannot be applied to async functions. Use #[cotis_start_async] instead.",
)
.to_compile_error()
.into();
}
let fn_name = &input_fn.sig.ident;
let expanded = quote! {
#input_fn
#[doc(hidden)]
#[unsafe(no_mangle)]
pub extern "Rust" fn __cotis_launch_hook() {
#fn_name();
}
};
expanded.into()
}
#[proc_macro_attribute]
pub fn cotis_start_async(_attr: TokenStream, item: TokenStream) -> TokenStream {
let input_fn = parse_macro_input!(item as ItemFn);
if !input_fn.sig.inputs.is_empty() {
return syn::Error::new_spanned(
&input_fn.sig,
"#[cotis_start_async] functions must not take parameters",
)
.to_compile_error()
.into();
}
let fn_name = &input_fn.sig.ident;
let hook_body = if input_fn.sig.asyncness.is_some() {
quote! {
Box::new(#fn_name())
}
} else {
quote! {
Box::new(async move {
#fn_name();
})
}
};
let expanded = quote! {
#input_fn
#[doc(hidden)]
#[unsafe(no_mangle)]
pub extern "Rust" fn __cotis_launch_async_hook() -> Box<dyn ::core::future::Future<Output = ()>> {
#hook_body
}
};
expanded.into()
}