tako_rs_macros/lib.rs
1//! Proc macros for the tako-rs framework.
2//!
3//! Provides [`route`], an attribute macro placed directly above an async
4//! handler function. Given an HTTP method and a path with `{name: Type}`
5//! placeholders, it generates a sibling `pub struct` whose fields exactly
6//! mirror the placeholders, plus:
7//!
8//! - `pub const METHOD: tako::Method` and `pub const PATH: &'static str`
9//! - an `impl TypedParamsStruct` that pulls each field from the request's
10//! `PathParams` extension and parses it via [`core::str::FromStr`]
11//!
12//! The struct name is auto-derived from the handler function's name
13//! (`snake_case` → `PascalCase` + `Params`). For example, `get_user` produces
14//! `GetUserParams`. Override the default with `name = "..."` if you need a
15//! different identifier.
16//!
17//! Method-specific shortcuts ([`get`], [`post`], [`put`], [`delete`],
18//! [`patch`]) take only the path and an optional `name = "..."`.
19//!
20//! Usage:
21//!
22//! ```ignore
23//! use tako::{get, route};
24//! use tako::extractors::typed_params::TypedParams;
25//! use tako::responder::Responder;
26//!
27//! #[route(GET, "/users/{id: u64}/posts/{post_id: u64}")]
28//! async fn get_user(TypedParams(p): TypedParams<GetUserParams>) -> impl Responder {
29//! format!("user {} post {}", p.id, p.post_id)
30//! }
31//!
32//! #[get("/health")]
33//! async fn health() -> impl Responder { "ok" }
34//!
35//! // …in build_router:
36//! // router.route(GetUserParams::METHOD, GetUserParams::PATH, get_user);
37//! // router.route(HealthParams::METHOD, HealthParams::PATH, health);
38//! ```
39//!
40//! The macro must be attached to a free async fn at module scope — Rust
41//! scopes structs declared inside fn bodies to that fn, so the generated
42//! type wouldn't be reachable from the handler signature otherwise.
43
44mod expand;
45mod parse;
46
47use proc_macro::TokenStream;
48use syn::ItemFn;
49use syn::parse_macro_input;
50
51use crate::expand::expand_route;
52use crate::expand::shortcut;
53use crate::parse::RouteArgs;
54
55#[proc_macro_attribute]
56pub fn route(attr: TokenStream, item: TokenStream) -> TokenStream {
57 let RouteArgs {
58 method,
59 path,
60 name_override,
61 } = parse_macro_input!(attr as RouteArgs);
62 let func = parse_macro_input!(item as ItemFn);
63 expand_route(method, path, name_override, func)
64}
65
66/// `#[get("/path", [name = "Foo"])]` — shorthand for `#[route(GET, ...)]`.
67#[proc_macro_attribute]
68pub fn get(attr: TokenStream, item: TokenStream) -> TokenStream {
69 shortcut("GET", attr, item)
70}
71
72/// `#[post("/path", [name = "Foo"])]` — shorthand for `#[route(POST, ...)]`.
73#[proc_macro_attribute]
74pub fn post(attr: TokenStream, item: TokenStream) -> TokenStream {
75 shortcut("POST", attr, item)
76}
77
78/// `#[put("/path", [name = "Foo"])]` — shorthand for `#[route(PUT, ...)]`.
79#[proc_macro_attribute]
80pub fn put(attr: TokenStream, item: TokenStream) -> TokenStream {
81 shortcut("PUT", attr, item)
82}
83
84/// `#[delete("/path", [name = "Foo"])]` — shorthand for `#[route(DELETE, ...)]`.
85#[proc_macro_attribute]
86pub fn delete(attr: TokenStream, item: TokenStream) -> TokenStream {
87 shortcut("DELETE", attr, item)
88}
89
90/// `#[patch("/path", [name = "Foo"])]` — shorthand for `#[route(PATCH, ...)]`.
91#[proc_macro_attribute]
92pub fn patch(attr: TokenStream, item: TokenStream) -> TokenStream {
93 shortcut("PATCH", attr, item)
94}