mira_macros/lib.rs
1//! Procedural macros for [Mira](https://docs.rs/mira-eval).
2//!
3//! This crate is an implementation detail of `mira-eval`; use it through the
4//! re-export `mira::eval`, not directly.
5
6use proc_macro::TokenStream;
7use quote::quote;
8use syn::{ItemFn, parse_macro_input};
9
10/// Register an eval factory for `cargo test`-style discovery.
11///
12/// `#[eval]` annotates a `fn() -> Eval` (or `-> EvalBuilder`) factory. It leaves
13/// the function untouched and additionally submits it to the global registry, so
14/// [`Study::registered`] / [`registered_evals`] pick it up with no central list.
15/// It is the ergonomic form of [`register_eval!`]; these are equivalent:
16///
17/// ```ignore
18/// #[eval]
19/// fn greet() -> Eval { /* … */ }
20///
21/// // …is the same as:
22/// fn greet() -> Eval { /* … */ }
23/// register_eval!(greet);
24/// ```
25///
26/// [`Study::registered`]: ../mira/struct.Study.html#method.registered
27/// [`registered_evals`]: ../mira/fn.registered_evals.html
28/// [`register_eval!`]: ../mira/macro.register_eval.html
29#[proc_macro_attribute]
30pub fn eval(args: TokenStream, item: TokenStream) -> TokenStream {
31 if !args.is_empty() {
32 let msg = "#[eval] takes no arguments; configure the eval inside the function body \
33 (e.g. `.targets(...)`, `.axis(...)`)";
34 let err = syn::Error::new(proc_macro2::Span::call_site(), msg);
35 return err.to_compile_error().into();
36 }
37
38 let func = parse_macro_input!(item as ItemFn);
39 let name = &func.sig.ident;
40
41 quote! {
42 #func
43 ::mira::register_eval!(#name);
44 }
45 .into()
46}