Skip to main content

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}