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