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}