Skip to main content

doido_controller_macros/
lib.rs

1mod api_mode;
2mod codegen;
3mod controller;
4mod helper;
5mod parser;
6
7use proc_macro::TokenStream;
8use syn::parse_macro_input;
9
10/// Expands the `routes!` DSL (verbs, `resources!`, `namespace!`, `scope!`)
11/// into an `axum::Router`. Merged in from the former `doido-router` crate.
12#[proc_macro]
13pub fn routes(input: TokenStream) -> TokenStream {
14    let parsed = parse_macro_input!(input as parser::RoutesInput);
15    codegen::generate(parsed).into()
16}
17
18/// Marks an impl block as a controller. Rewrites action methods into
19/// axum-compatible handlers, wiring in any filters.
20///
21/// Filters are declared with the `#[before_action(...)]` / `#[after_action(...)]`
22/// **helper attributes** on action methods; this macro parses and consumes them
23/// while expanding the impl block, so there are no standalone
24/// `before_action`/`after_action` macros to import:
25///
26/// ```ignore
27/// #[controller]
28/// impl PostsController {
29///     #[before_action(require_auth)]
30///     #[before_action(load_record, only = [show, edit])]
31///     #[after_action(log_response)]
32///     async fn show(ctx: &mut Context) -> Response { /* ... */ }
33/// }
34/// ```
35///
36/// `before_action` filters run in declaration order before the action and may
37/// short-circuit by returning `Err(response)`; `after_action` filters run after.
38#[proc_macro_attribute]
39pub fn controller(attr: TokenStream, item: TokenStream) -> TokenStream {
40    match controller::expand_controller(attr.into(), item.into()) {
41        Ok(ts) => ts.into(),
42        Err(e) => e.to_compile_error().into(),
43    }
44}
45
46/// Marks a struct as a controller helper. Generates a [`doido_controller::Helper`]
47/// implementation that carries the snake_case helper name (`PostsHelper` →
48/// `"posts_helper"`), matching the `app/helpers/<name>_helper.rs` convention.
49/// Import the helper in controllers and call its associated functions.
50///
51/// ```ignore
52/// #[helper]
53/// pub struct PostsHelper;
54///
55/// impl PostsHelper {
56///     pub fn format_title(title: &str) -> String { title.to_uppercase() }
57/// }
58/// ```
59#[proc_macro_attribute]
60pub fn helper(attr: TokenStream, item: TokenStream) -> TokenStream {
61    match helper::expand_helper(attr.into(), item.into()) {
62        Ok(ts) => ts.into(),
63        Err(e) => e.to_compile_error().into(),
64    }
65}