rocket_community/catcher/handler.rs
1use crate::http::Status;
2use crate::{Request, Response};
3
4/// Type alias for the return type of a [`Catcher`](crate::Catcher)'s
5/// [`Handler::handle()`].
6pub type Result<'r> = std::result::Result<Response<'r>, crate::http::Status>;
7
8/// Type alias for the return type of a _raw_ [`Catcher`](crate::Catcher)'s
9/// [`Handler`].
10pub type BoxFuture<'r, T = Result<'r>> = futures::future::BoxFuture<'r, T>;
11
12/// Trait implemented by [`Catcher`](crate::Catcher) error handlers.
13///
14/// This trait is exactly like a [`Route`](crate::Route)'s
15/// [`Handler`](crate::route::Handler) except it handles errors instead of
16/// requests. Thus, the documentation for
17/// [`route::Handler`](crate::route::Handler) applies to this trait as well. We
18/// defer to it for full details.
19///
20/// ## Async Trait
21///
22/// This is an _async_ trait. Implementations must be decorated
23/// [`#[rocket::async_trait]`](crate::async_trait).
24///
25/// # Example
26///
27/// Say you'd like to write a handler that changes its functionality based on a
28/// `Kind` enum value that the user provides. Such a handler might be written
29/// and used as follows:
30///
31/// ```rust,no_run
32/// # extern crate rocket_community as rocket;
33/// use rocket::{Request, Catcher, catcher};
34/// use rocket::response::{Response, Responder};
35/// use rocket::http::Status;
36///
37/// #[derive(Copy, Clone)]
38/// enum Kind {
39/// Simple,
40/// Intermediate,
41/// Complex,
42/// }
43///
44/// #[derive(Clone)]
45/// struct CustomHandler(Kind);
46///
47/// #[rocket::async_trait]
48/// impl catcher::Handler for CustomHandler {
49/// async fn handle<'r>(&self, status: Status, req: &'r Request<'_>) -> catcher::Result<'r> {
50/// let inner = match self.0 {
51/// Kind::Simple => "simple".respond_to(req)?,
52/// Kind::Intermediate => "intermediate".respond_to(req)?,
53/// Kind::Complex => "complex".respond_to(req)?,
54/// };
55///
56/// Response::build_from(inner).status(status).ok()
57/// }
58/// }
59///
60/// impl CustomHandler {
61/// /// Returns a `default` catcher that uses `CustomHandler`.
62/// fn default(kind: Kind) -> Vec<Catcher> {
63/// vec![Catcher::new(None, CustomHandler(kind))]
64/// }
65///
66/// /// Returns a catcher for code `status` that uses `CustomHandler`.
67/// fn catch(status: Status, kind: Kind) -> Vec<Catcher> {
68/// vec![Catcher::new(status.code, CustomHandler(kind))]
69/// }
70/// }
71///
72/// #[rocket::launch]
73/// fn rocket() -> _ {
74/// rocket::build()
75/// // to handle only `404`
76/// .register("/", CustomHandler::catch(Status::NotFound, Kind::Simple))
77/// // or to register as the default
78/// .register("/", CustomHandler::default(Kind::Simple))
79/// }
80/// ```
81///
82/// Note the following:
83///
84/// 1. `CustomHandler` implements `Clone`. This is required so that
85/// `CustomHandler` implements `Cloneable` automatically. The `Cloneable`
86/// trait serves no other purpose but to ensure that every `Handler`
87/// can be cloned, allowing `Catcher`s to be cloned.
88/// 2. `CustomHandler`'s methods return `Vec<Route>`, allowing for use
89/// directly as the parameter to `rocket.register("/", )`.
90/// 3. Unlike static-function-based handlers, this custom handler can make use
91/// of internal state.
92#[crate::async_trait]
93pub trait Handler: Cloneable + Send + Sync + 'static {
94 /// Called by Rocket when an error with `status` for a given `Request`
95 /// should be handled by this handler.
96 ///
97 /// Error handlers _should not_ fail and thus _should_ always return `Ok`.
98 /// Nevertheless, failure is allowed, both for convenience and necessity. If
99 /// an error handler fails, Rocket's default `500` catcher is invoked. If it
100 /// succeeds, the returned `Response` is used to respond to the client.
101 async fn handle<'r>(&self, status: Status, req: &'r Request<'_>) -> Result<'r>;
102}
103
104// We write this manually to avoid double-boxing.
105impl<F: Clone + Sync + Send + 'static> Handler for F
106where
107 for<'x> F: Fn(Status, &'x Request<'_>) -> BoxFuture<'x>,
108{
109 fn handle<'r, 'life0, 'life1, 'async_trait>(
110 &'life0 self,
111 status: Status,
112 req: &'r Request<'life1>,
113 ) -> BoxFuture<'r>
114 where
115 'r: 'async_trait,
116 'life0: 'async_trait,
117 'life1: 'async_trait,
118 Self: 'async_trait,
119 {
120 self(status, req)
121 }
122}
123
124// Used in tests! Do not use, please.
125#[doc(hidden)]
126pub fn dummy_handler<'r>(_: Status, _: &'r Request<'_>) -> BoxFuture<'r> {
127 Box::pin(async move { Ok(Response::new()) })
128}
129
130mod private {
131 pub trait Sealed {}
132 impl<T: super::Handler + Clone> Sealed for T {}
133}
134
135/// Helper trait to make a [`Catcher`](crate::Catcher)'s `Box<dyn Handler>`
136/// `Clone`.
137///
138/// This trait cannot be implemented directly. Instead, implement `Clone` and
139/// [`Handler`]; all types that implement `Clone` and `Handler` automatically
140/// implement `Cloneable`.
141pub trait Cloneable: private::Sealed {
142 #[doc(hidden)]
143 fn clone_handler(&self) -> Box<dyn Handler>;
144}
145
146impl<T: Handler + Clone> Cloneable for T {
147 fn clone_handler(&self) -> Box<dyn Handler> {
148 Box::new(self.clone())
149 }
150}
151
152impl Clone for Box<dyn Handler> {
153 fn clone(&self) -> Box<dyn Handler> {
154 self.clone_handler()
155 }
156}