Skip to main content

go_lib_macros/
lib.rs

1// SPDX-License-Identifier: Apache-2.0
2//! Procedural macros for go-lib.
3//!
4//! Exported through `go_lib` — use as `#[go_lib::main]`.
5
6use proc_macro::TokenStream;
7use quote::quote;
8use syn::{parse_macro_input, ItemFn, ReturnType};
9
10/// Attribute macro that promotes a function's body into the program's first
11/// goroutine on the process-wide go-lib scheduler.
12///
13/// This is the single blessed entry point: apply it to `fn main` (or any
14/// entry function) instead of manually wrapping the body. The scheduler is a
15/// process singleton, initialised on first use; the attribute runs the body
16/// as the "main goroutine" and blocks the calling OS thread until it returns,
17/// mirroring Go's `main`.
18///
19/// The macro rewrites
20///
21/// ```rust,ignore
22/// #[go_lib::main]
23/// fn main() {
24///     /* body */
25/// }
26/// ```
27///
28/// into
29///
30/// ```rust,ignore
31/// fn main() {
32///     go_lib::__main_entry(move || {
33///         /* body */
34///     })
35/// }
36/// ```
37///
38/// When the function has a return type the closure is annotated with the same
39/// type so that `?` and explicit `return` expressions work as expected:
40///
41/// ```rust,ignore
42/// #[go_lib::main]
43/// fn main() -> Result<(), MyError> {
44///     do_work()?;
45///     Ok(())
46/// }
47/// // expands to:
48/// fn main() -> Result<(), MyError> {
49///     go_lib::__main_entry(move || -> Result<(), MyError> {
50///         do_work()?;
51///         Ok(())
52///     })
53/// }
54/// ```
55///
56/// Function parameters (if any) are captured by the `move` closure, so the
57/// macro also works on non-`main` entry points or helper functions.
58///
59/// # Errors
60///
61/// Emits a compile error if the function is `async` (go-lib provides its own
62/// concurrency model and does not interact with an async executor).
63#[proc_macro_attribute]
64pub fn main(_attr: TokenStream, item: TokenStream) -> TokenStream {
65    let mut func = parse_macro_input!(item as ItemFn);
66
67    // Reject async functions — go-lib's scheduler is not an async executor.
68    if let Some(async_token) = &func.sig.asyncness {
69        return syn::Error::new_spanned(
70            async_token,
71            "#[go_lib::main] does not support async functions; \
72             go-lib provides its own M:N scheduler",
73        )
74        .to_compile_error()
75        .into();
76    }
77
78    let return_type = func.sig.output.clone();
79    let body        = &*func.block;
80
81    // Build `go_lib::__main_entry(move || [-> ReturnType] { body })`.
82    // The `move` ensures function parameters are captured into the closure.
83    let run_call = match &return_type {
84        ReturnType::Default => quote! {
85            go_lib::__main_entry(move || #body)
86        },
87        ReturnType::Type(_, ty) => quote! {
88            go_lib::__main_entry(move || -> #ty #body)
89        },
90    };
91
92    // Replace the function body with the single `run` call.
93    func.block = syn::parse_quote! { { #run_call } };
94
95    quote! { #func }.into()
96}