rust_web_server/macros/mod.rs
1//! Declarative routing macro.
2//!
3//! The [`routes!`] macro builds an [`crate::state::AppWithState`],
4//! [`crate::async_state::AsyncAppWithState`], or any other builder that
5//! exposes `.get()`, `.post()`, `.put()`, `.patch()`, and `.delete()` methods.
6
7#[cfg(test)]
8mod tests;
9
10/// Build a routing app from a declarative table.
11///
12/// Syntax:
13/// ```text
14/// routes! {
15/// <builder>,
16/// METHOD "path" => handler,
17/// ...
18/// }
19/// ```
20///
21/// `METHOD` must be one of `GET`, `POST`, `PUT`, `PATCH`, or `DELETE` (all
22/// caps). The builder receives one `.method(path, handler)` call per entry,
23/// chained in declaration order.
24///
25/// Handlers may be named functions or closures — anything accepted by the
26/// corresponding builder method. A trailing comma after the last route is
27/// optional.
28///
29/// # Example — stateful sync app
30///
31/// ```rust
32/// use rust_web_server::app::App;
33/// use rust_web_server::core::New;
34/// use rust_web_server::routes;
35/// use rust_web_server::request::Request;
36/// use rust_web_server::router::PathParams;
37/// use rust_web_server::server::ConnectionInfo;
38/// use rust_web_server::response::{Response, STATUS_CODE_REASON_PHRASE};
39///
40/// struct Db;
41///
42/// // AppWithState<S> passes &S (not &Arc<S>) to the handler.
43/// fn list_users(
44/// _req: &Request,
45/// _params: &PathParams,
46/// _conn: &ConnectionInfo,
47/// _state: &Db,
48/// ) -> Response {
49/// let mut r = Response::new();
50/// r.status_code = *STATUS_CODE_REASON_PHRASE.n200_ok.status_code;
51/// r.reason_phrase = STATUS_CODE_REASON_PHRASE.n200_ok.reason_phrase.to_string();
52/// r
53/// }
54///
55/// fn create_user(
56/// _req: &Request,
57/// _params: &PathParams,
58/// _conn: &ConnectionInfo,
59/// _state: &Db,
60/// ) -> Response {
61/// let mut r = Response::new();
62/// r.status_code = *STATUS_CODE_REASON_PHRASE.n201_created.status_code;
63/// r.reason_phrase = STATUS_CODE_REASON_PHRASE.n201_created.reason_phrase.to_string();
64/// r
65/// }
66///
67/// let app = routes! {
68/// App::with_state(Db),
69/// GET "/users" => list_users,
70/// POST "/users" => create_user,
71/// };
72/// ```
73///
74/// # Example — inline closures
75///
76/// ```rust
77/// use rust_web_server::app::App;
78/// use rust_web_server::core::New;
79/// use rust_web_server::routes;
80/// use rust_web_server::response::{Response, STATUS_CODE_REASON_PHRASE};
81///
82/// let app = routes! {
83/// App::with_state(42u32),
84/// GET "/ping" => |_req, _params, _conn, _state: &u32| {
85/// let mut r = Response::new();
86/// r.status_code = *STATUS_CODE_REASON_PHRASE.n200_ok.status_code;
87/// r.reason_phrase = STATUS_CODE_REASON_PHRASE.n200_ok.reason_phrase.to_string();
88/// r
89/// },
90/// };
91/// ```
92#[macro_export]
93macro_rules! routes {
94 // Base case — no routes left (handles optional trailing comma).
95 ($app:expr $(,)?) => { $app };
96
97 ($app:expr, GET $path:literal => $handler:expr $(, $($tail:tt)*)?) => {
98 $crate::routes!($app.get($path, $handler) $(, $($tail)*)?)
99 };
100 ($app:expr, POST $path:literal => $handler:expr $(, $($tail:tt)*)?) => {
101 $crate::routes!($app.post($path, $handler) $(, $($tail)*)?)
102 };
103 ($app:expr, PUT $path:literal => $handler:expr $(, $($tail:tt)*)?) => {
104 $crate::routes!($app.put($path, $handler) $(, $($tail)*)?)
105 };
106 ($app:expr, PATCH $path:literal => $handler:expr $(, $($tail:tt)*)?) => {
107 $crate::routes!($app.patch($path, $handler) $(, $($tail)*)?)
108 };
109 ($app:expr, DELETE $path:literal => $handler:expr $(, $($tail:tt)*)?) => {
110 $crate::routes!($app.delete($path, $handler) $(, $($tail)*)?)
111 };
112}