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::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}