Skip to main content

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}