trillium_router/router_ref.rs
1use crate::Router;
2use routefinder::RouteSpec;
3use std::fmt::Debug;
4use trillium::{Handler, Method};
5
6macro_rules! method_ref {
7 ($fn_name:ident, $method:ident) => {
8 method_ref!(
9 $fn_name,
10 $method,
11 concat!(
12 // yep, macro-generated doctests
13 "Registers a handler for the ",
14 stringify!($fn_name),
15 " http method.
16
17```
18# use trillium::Conn;
19# use trillium_router::Router;
20# use trillium_testing::TestServer;
21# trillium_testing::block_on(async {
22let router = Router::build(|mut router| {
23 router.",
24 stringify!($fn_name),
25 "(\"/some/route\", |conn: Conn| async move {
26 conn.ok(\"success\")
27 });
28});
29
30let app = TestServer::new(router).await;
31app.",
32 stringify!($fn_name),
33 "(\"/some/route\").await
34 .assert_ok()
35 .assert_body(\"success\");
36app.",
37 stringify!($fn_name),
38 "(\"/other/route\").await
39 .assert_status(404);
40# });
41```
42"
43 )
44 );
45 };
46
47 ($fn_name:ident, $method:ident, $doc_comment:expr_2021) => {
48 #[doc = $doc_comment]
49 pub fn $fn_name<R>(&mut self, path: R, handler: impl Handler)
50 where
51 R: TryInto<RouteSpec>,
52 R::Error: Debug,
53 {
54 self.0.add(path, Method::$method, handler);
55 }
56 };
57}
58
59/// # A `&mut Router` for use with `Router::build`
60///
61/// A wrapper around a `&mut Router` that supports imperative route
62/// registration. See [`Router::build`] for further documentation.
63#[derive(Debug)]
64pub struct RouterRef<'r>(&'r mut Router);
65impl<'r> RouterRef<'r> {
66 method_ref!(get, Get);
67
68 method_ref!(post, Post);
69
70 method_ref!(put, Put);
71
72 method_ref!(delete, Delete);
73
74 method_ref!(patch, Patch);
75
76 /// Appends the handler to all (get, post, put, delete, and patch) methods.
77 ///
78 /// ```
79 /// # use trillium::Conn;
80 /// # use trillium_router::Router;
81 /// # use trillium_testing::TestServer;
82 /// # trillium_testing::block_on(async {
83 /// let router = Router::build(|mut router| {
84 /// router.all("/any", |conn: Conn| async move {
85 /// let response = format!("you made a {} request to /any", conn.method());
86 /// conn.ok(response)
87 /// });
88 /// });
89 ///
90 /// let app = TestServer::new(router).await;
91 /// app.get("/any")
92 /// .await
93 /// .assert_ok()
94 /// .assert_body("you made a GET request to /any");
95 /// app.post("/any")
96 /// .await
97 /// .assert_ok()
98 /// .assert_body("you made a POST request to /any");
99 /// app.delete("/any")
100 /// .await
101 /// .assert_ok()
102 /// .assert_body("you made a DELETE request to /any");
103 /// app.patch("/any")
104 /// .await
105 /// .assert_ok()
106 /// .assert_body("you made a PATCH request to /any");
107 /// app.put("/any")
108 /// .await
109 /// .assert_ok()
110 /// .assert_body("you made a PUT request to /any");
111 /// app.get("/").await.assert_status(404);
112 /// # });
113 /// ```
114 pub fn all<R>(&mut self, path: R, handler: impl Handler)
115 where
116 R: TryInto<RouteSpec>,
117 R::Error: Debug,
118 {
119 self.0.add_all(path, handler)
120 }
121
122 /// Appends the handler to each of the provided http methods.
123 /// ```
124 /// # use trillium::Conn;
125 /// # use trillium_router::Router;
126 /// # use trillium_testing::TestServer;
127 /// # trillium_testing::block_on(async {
128 /// let router = Router::build(|mut router| {
129 /// router.any(&["get", "post"], "/get_or_post", |conn: Conn| async move {
130 /// let response = format!("you made a {} request to /get_or_post", conn.method());
131 /// conn.ok(response)
132 /// });
133 /// });
134 ///
135 /// let app = TestServer::new(router).await;
136 /// app.get("/get_or_post")
137 /// .await
138 /// .assert_ok()
139 /// .assert_body("you made a GET request to /get_or_post");
140 /// app.post("/get_or_post")
141 /// .await
142 /// .assert_ok()
143 /// .assert_body("you made a POST request to /get_or_post");
144 /// app.delete("/any").await.assert_status(404);
145 /// app.patch("/any").await.assert_status(404);
146 /// app.put("/any").await.assert_status(404);
147 /// app.get("/").await.assert_status(404);
148 /// # });
149 /// ```
150 pub fn any<IntoMethod, R>(&mut self, methods: &[IntoMethod], path: R, handler: impl Handler)
151 where
152 R: TryInto<RouteSpec>,
153 R::Error: Debug,
154 IntoMethod: TryInto<Method> + Clone,
155 <IntoMethod as TryInto<Method>>::Error: Debug,
156 {
157 let methods = methods
158 .iter()
159 .cloned()
160 .map(|m| m.try_into().unwrap())
161 .collect::<Vec<_>>();
162
163 self.0.add_any(&methods, path, handler);
164 }
165
166 pub(crate) fn new(router: &'r mut Router) -> Self {
167 Self(router)
168 }
169
170 /// Registers a handler for a method other than get, put, post, patch, or delete.
171 /// ```
172 /// # use trillium::{Conn, Method};
173 /// # use trillium_router::Router;
174 /// # use trillium_testing::TestServer;
175 /// # trillium_testing::block_on(async {
176 /// let router = Router::build(|mut router| {
177 /// router.add_route("OPTIONS", "/some/route", |conn: Conn| async move {
178 /// conn.ok("directly handling options")
179 /// });
180 ///
181 /// router.add_route(Method::Checkin, "/some/route", |conn: Conn| async move {
182 /// conn.ok("checkin??")
183 /// });
184 /// });
185 ///
186 /// let app = TestServer::new(router).await;
187 /// app.build(Method::Options, "/some/route")
188 /// .await
189 /// .assert_ok()
190 /// .assert_body("directly handling options");
191 /// app.build(Method::Checkin, "/some/route")
192 /// .await
193 /// .assert_ok()
194 /// .assert_body("checkin??");
195 /// # });
196 /// ```
197 pub fn add_route<M, R>(&mut self, method: M, path: R, handler: impl Handler)
198 where
199 M: TryInto<Method>,
200 <M as TryInto<Method>>::Error: Debug,
201 R: TryInto<RouteSpec>,
202 R::Error: Debug,
203 {
204 self.0.add(path, method.try_into().unwrap(), handler);
205 }
206
207 /// enable or disable the router's behavior of responding to OPTIONS
208 /// requests with the supported methods at given path.
209 ///
210 /// default: enabled
211 ///
212 /// see crate-level docs for further explanation of the default behavior.
213 pub fn set_options_handling(&mut self, options_enabled: bool) {
214 self.0.set_options_handling(options_enabled);
215 }
216}