Skip to main content

rmlx_macros/
lib.rs

1//! Proc macros for RMLX kernel registration.
2//!
3//! Provides `#[rmlx_kernel(...)]` to auto-generate kernel registration boilerplate.
4
5use proc_macro::TokenStream;
6use quote::quote;
7use syn::{parse_macro_input, punctuated::Punctuated, ItemFn, Meta, Token};
8
9/// Attribute macro for kernel registration functions.
10///
11/// # Usage
12///
13/// ```ignore
14/// #[rmlx_kernel(name = "my_kernel")]
15/// pub fn register_my_kernel(registry: &KernelRegistry) -> Result<(), KernelError> {
16///     // Body is preserved as-is, macro just adds registration metadata
17/// }
18/// ```
19///
20/// This is an exploratory prototype. Currently it:
21/// 1. Validates the function signature has exactly one parameter
22/// 2. Generates a companion `_kernel_metadata()` function returning the kernel name
23/// 3. Preserves the original function unchanged
24#[proc_macro_attribute]
25pub fn rmlx_kernel(attr: TokenStream, item: TokenStream) -> TokenStream {
26    let input = parse_macro_input!(item as ItemFn);
27    let attrs = parse_macro_input!(attr with Punctuated::<Meta, Token![,]>::parse_terminated);
28
29    let fn_name = &input.sig.ident;
30
31    // Validate: function must have exactly one parameter (the registry).
32    if input.sig.inputs.len() != 1 {
33        return syn::Error::new_spanned(
34            &input.sig,
35            "rmlx_kernel: expected exactly one parameter (registry: &KernelRegistry)",
36        )
37        .to_compile_error()
38        .into();
39    }
40
41    // Extract kernel name from attributes, fall back to function name.
42    let mut kernel_name: Option<String> = None;
43    for meta in &attrs {
44        if let Meta::NameValue(nv) = meta {
45            if nv.path.is_ident("name") {
46                if let syn::Expr::Lit(syn::ExprLit {
47                    lit: syn::Lit::Str(s),
48                    ..
49                }) = &nv.value
50                {
51                    kernel_name = Some(s.value());
52                }
53            }
54        }
55    }
56
57    let kernel_name_str = kernel_name.unwrap_or_else(|| fn_name.to_string());
58    let metadata_fn_name = syn::Ident::new(&format!("{fn_name}_kernel_metadata"), fn_name.span());
59
60    let output = quote! {
61        #input
62
63        /// Auto-generated kernel metadata.
64        #[doc(hidden)]
65        pub fn #metadata_fn_name() -> &'static str {
66            #kernel_name_str
67        }
68    };
69
70    output.into()
71}