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}