nest_rs_http_macros/lib.rs
1//! HTTP attribute macros, re-exported by `nestrs-http`. Generated code uses
2//! absolute paths (`::nest_rs_http::*`, `::poem::*`, `::nest_rs_core::*`), so
3//! this crate has no dependency on its surface crate — they resolve at the
4//! call site.
5
6use proc_macro::TokenStream;
7
8mod attr;
9mod controller;
10mod crud;
11mod input;
12mod interceptor;
13mod response;
14mod routes;
15
16/// `#[controller(path = "/health")]` — paired with `#[routes]` on the impl
17/// block. Generates `from_container(&Container) -> Self` and a `pub const PATH`.
18///
19/// Class-level `#[use_guards(...)]` / `#[use_filters(...)]` /
20/// `#[use_interceptors(...)]` placed *below* `#[controller]` apply to every
21/// route the controller mounts; they stack *outside* any per-route binding
22/// (first listed outermost). An optional `version = "1"` enables URI versioning
23/// — see [`version_path`](::nest_rs_http::version_path).
24///
25/// The `Discoverable` impl is emitted by `#[routes]` (which owns the route
26/// table), not here.
27#[proc_macro_attribute]
28pub fn controller(args: TokenStream, input: TokenStream) -> TokenStream {
29 controller::controller(args, input)
30}
31
32/// Mark a struct as a **global** HTTP interceptor. Behaves like `#[injectable]`
33/// for construction and additionally emits a `Discoverable` impl attaching an
34/// `HttpEndpointWrap`; the HTTP transport reads those metas at boot. The
35/// struct must implement `nest_rs_interceptors::Interceptor`.
36#[proc_macro_attribute]
37pub fn interceptor(args: TokenStream, input: TokenStream) -> TokenStream {
38 interceptor::interceptor(args, input)
39}
40
41/// Bind controller methods to HTTP routes. Applied to an `impl` block
42/// belonging to a `#[controller]`-marked struct. Each method tagged with
43/// `#[get("/path")]`, `#[post]`, `#[put]`, `#[delete]`, or `#[patch]` is wired
44/// as a poem handler.
45///
46/// Per-method attributes (all consumed; no imports needed):
47///
48/// - `#[use_guards(...)]` — container-resolved guards, first listed outermost.
49/// - `#[use_filters(...)]` — exception filters, wrap *outside* the guards.
50/// - `#[use_interceptors(...)]` — container-resolved interceptors.
51/// - `#[meta(EXPR)]` (repeatable) — typed metadata read back by a guard with
52/// `nest_rs_http::Reflector` (value type: `Clone + Send + Sync + 'static`).
53/// - `#[api(summary, description, tags(...))]` — OpenAPI facets.
54///
55/// The macro also reads each handler's signature and records the schema of any
56/// `Json<T>` request body / response into the route's `HttpRouteMeta` (`T:
57/// nest_rs_http::schemars::JsonSchema`); raw `Response`/`String` returns carry
58/// no schema.
59///
60/// Emits `nest_rs_http::Controller` (mount entry point) and
61/// `nest_rs_core::Discoverable` (attaches the route table + mount closure).
62#[proc_macro_attribute]
63pub fn routes(args: TokenStream, input: TokenStream) -> TokenStream {
64 routes::routes(args, input)
65}
66
67/// Generate standard REST operations (list/get/create/update/delete) on a
68/// `#[controller]` impl block, re-emitting under `#[routes]`. Grammar:
69/// `#[crud(entity = …::Entity, output = Dto, create = CreateDto,
70/// update = UpdateDto, readonly, paginate = cursor|page)]`.
71///
72/// Guards are declared once on the controller (`#[use_guards(...)]` on the
73/// struct) — every generated route inherits them. A hand-written
74/// `list`/`get`/`create`/`update`/`delete` method overrides its generated
75/// counterpart.
76#[proc_macro_attribute]
77pub fn crud(args: TokenStream, input: TokenStream) -> TokenStream {
78 crud::entry(args, input)
79}
80
81/// `#[input]` — shorthand for input DTOs. Appends
82/// `#[derive(::serde::Deserialize, ::validator::Validate)]` and
83/// `#[serde(deny_unknown_fields)]` so an unknown field on the wire
84/// (e.g. `is_admin: true`) is rejected at parse time instead of silently
85/// dropped.
86#[proc_macro_attribute]
87pub fn input(args: TokenStream, item: TokenStream) -> TokenStream {
88 input::input(args, item)
89}
90
91/// `#[http_code(N)]` — override the response status (`100..=999`). Passthrough
92/// marker consumed by `#[routes]`. Mutually exclusive with `#[redirect]`.
93#[proc_macro_attribute]
94pub fn http_code(args: TokenStream, item: TokenStream) -> TokenStream {
95 response::passthrough(args, item)
96}
97
98/// `#[response_header("name", "value")]` — append a header to the response.
99/// Stacks with `#[http_code]` and `#[redirect]`; repeatable. Passthrough
100/// marker consumed by `#[routes]`.
101#[proc_macro_attribute]
102pub fn response_header(args: TokenStream, item: TokenStream) -> TokenStream {
103 response::passthrough(args, item)
104}
105
106/// `#[redirect("url"[, code])]` — discard the handler's payload and return a
107/// redirect. Status defaults to `307` and must be in `300..=399`. Mutually
108/// exclusive with `#[http_code]`. The decorated method's body must be empty
109/// — `#[routes]` does not call it. Passthrough marker consumed by `#[routes]`.
110#[proc_macro_attribute]
111pub fn redirect(args: TokenStream, item: TokenStream) -> TokenStream {
112 response::passthrough(args, item)
113}