Skip to main content

moirai_async_macros/
lib.rs

1//! Attribute macro providing an async `main` for Moirai.
2//!
3//! Rust's entry point cannot itself be `async`, so something has to build
4//! a runtime and block on the future. This crate supplies that shim for
5//! Moirai, keeping executor construction out of user code.
6
7#![deny(missing_docs)]
8
9extern crate proc_macro;
10
11use proc_macro::TokenStream;
12
13/// Marks an `async fn main` as the program entry point.
14///
15/// Rewrites the annotated function into a synchronous `main` that builds
16/// an `AsyncExecutor` and blocks on the original body, preserving the
17/// function's attributes and visibility.
18///
19/// The attribute takes no arguments and the executor is constructed with
20/// its defaults; a caller needing a configured executor should build one
21/// directly and call `block_on` instead.
22///
23/// # Panics
24///
25/// The generated `main` panics if the executor cannot be created. That is
26/// deliberate: it runs before any user code, so there is no state to
27/// unwind and nothing useful to recover to.
28#[proc_macro_attribute]
29pub fn main(_attr: TokenStream, item: TokenStream) -> TokenStream {
30    let input: syn::ItemFn = syn::parse(item).expect("expected a function");
31
32    let fn_attrs = &input.attrs;
33    let fn_vis = &input.vis;
34    let fn_block = &input.block;
35
36    let result = quote::quote! {
37        #(#fn_attrs)*
38        #fn_vis fn main() {
39            let executor = moirai_async::executor::AsyncExecutor::new()
40                .expect("Failed to create Moirai async executor");
41            executor.block_on(async #fn_block);
42        }
43    };
44
45    result.into()
46}