churust_core/handler.rs
1//! Handlers: the [`Handler`] trait, handler closures, and the glue that turns
2//! extractor closures into stored handlers.
3//!
4//! Most route handlers are plain async closures (`|c: Call| async { ... }`, or
5//! a closure taking [extractors](crate::extract)). They reach the [`Router`] via
6//! [`IntoHandler`], which adapts a closure into a real [`Handler`] and lets it
7//! be [`boxed`] into a [`BoxHandler`] for storage.
8//!
9//! [`Router`]: crate::Router
10
11use crate::call::Call;
12use crate::response::{IntoResponse, Response};
13use std::future::Future;
14use std::pin::Pin;
15use std::sync::Arc;
16
17/// A boxed, `'static` future returned by a [`Handler`].
18///
19/// It is `'static` because a handler owns its [`Call`] (the call is moved in),
20/// so nothing is borrowed across the await point.
21pub type HandlerFuture = Pin<Box<dyn Future<Output = Response> + Send + 'static>>;
22
23/// Anything that can handle a [`Call`] and produce a [`Response`].
24///
25/// This is the type-erased shape the [`Router`](crate::Router) stores (as a
26/// [`BoxHandler`]). You rarely implement it by hand — async closures become
27/// `Handler`s through [`IntoHandler`] — but you can implement it directly for a
28/// type that needs to carry state.
29///
30/// ```
31/// use churust_core::{Call, Handler, Response, IntoResponse, handler::HandlerFuture};
32/// use http::{HeaderMap, Method};
33/// use bytes::Bytes;
34///
35/// struct Greeter;
36/// impl Handler for Greeter {
37/// fn handle(&self, _call: Call) -> HandlerFuture {
38/// Box::pin(async { "hi".into_response() })
39/// }
40/// }
41/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
42/// let call = Call::new(Method::GET, "/".parse().unwrap(), HeaderMap::new(), Bytes::new());
43/// let res = Greeter.handle(call).await;
44/// assert_eq!(res.body.as_slice(), Some(&b"hi"[..]));
45/// # });
46/// ```
47pub trait Handler: Send + Sync + 'static {
48 /// Handle the call, returning a future that resolves to the response.
49 fn handle(&self, call: Call) -> HandlerFuture;
50}
51
52use crate::extract::{FromCall, FromCallParts};
53
54/// Bridge trait implemented by handler closures of a given arity. The LAST
55/// closure argument is `FromCall` (consuming); all earlier ones are
56/// `FromCallParts` (borrowing). Handlers must be `Clone` so the closure can be
57/// moved into the `'static` response future.
58///
59/// IMPLEMENTATION NOTE (why this is not the spec's literal macro): the plan's
60/// Step 2 shows a macro that emits `impl<...> Handler for F` directly for each
61/// arity. That design does NOT compile on stable Rust: the coherence checker
62/// treats `impl Handler for F where F: Fn()`, `... F: Fn(A)`, `... F: Fn(A, B)`
63/// etc. as mutually overlapping, because a single closure type could in
64/// principle implement several `Fn` traits of different arities (E0119 on every
65/// arity). A disambiguating `Marker` type parameter is required to keep the
66/// per-arity impls disjoint — the standard axum-style pattern. The public
67/// `Handler` trait, `BoxHandler`, and `boxed` keep the spec signatures; this
68/// `HandlerFn` family is the closure-side bridge that is adapted into a
69/// `Handler` via [`IntoHandler`] (see below). This trait is `pub` only because
70/// it appears in the bound of `IntoHandler`/the router's method builders; it is
71/// intentionally NOT re-exported from the crate root.
72#[diagnostic::on_unimplemented(
73 message = "`{Self}` is not a Churust handler",
74 label = "not a handler",
75 note = "a handler is an `async fn` or a closure returning a future, taking up to 8 arguments",
76 note = "every argument except the last must implement `FromCallParts` (it sees only the request head)",
77 note = "the last argument may implement `FromCall` and consume the body — `Json`, `Form`, `Bytes`, `String`, `Payload`, `Multipart`, `Either`",
78 note = "two body-consuming arguments cannot compile: the body is a one-shot stream",
79 note = "the return type must implement `IntoResponse`"
80)]
81pub trait HandlerFn<Marker>: Clone + Send + Sync + 'static {
82 /// Run the closure against the call: extract each argument from the call in
83 /// order, then invoke the closure and convert its return value into a
84 /// [`Response`].
85 fn call(self, call: Call) -> HandlerFuture;
86}
87
88/// Generates a `HandlerFn` impl whose closure args are `($($P,)* L)` — the
89/// `$P` are the leading `FromCallParts` extractors and `L` is the final
90/// `FromCall` extractor. `impl_handler!()` (empty) produces the arity-1
91/// (last-arg-only) impl; the arity-0 impl is written out separately below.
92macro_rules! impl_handler {
93 ($($P:ident),*) => {
94 #[allow(non_snake_case, unused_mut, unused_variables)]
95 impl<F, Fut, R, $($P,)* L> HandlerFn<($($P,)* L,)> for F
96 where
97 F: Fn($($P,)* L) -> Fut + Clone + Send + Sync + 'static,
98 Fut: std::future::Future<Output = R> + Send + 'static,
99 R: IntoResponse + 'static,
100 $($P: FromCallParts + 'static,)*
101 L: FromCall + 'static,
102 {
103 fn call(self, call: Call) -> HandlerFuture {
104 let f = self;
105 Box::pin(async move {
106 let mut call = call;
107 $(
108 let $P = match <$P as FromCallParts>::from_call_parts(&mut call).await {
109 Ok(v) => v,
110 Err(e) => return e.into_response(),
111 };
112 )*
113 let last = match <L as FromCall>::from_call(call).await {
114 Ok(v) => v,
115 Err(e) => return e.into_response(),
116 };
117 f($($P,)* last).await.into_response()
118 })
119 }
120 }
121 };
122}
123
124// Arity 0: a parameterless closure.
125impl<F, Fut, R> HandlerFn<()> for F
126where
127 F: Fn() -> Fut + Clone + Send + Sync + 'static,
128 Fut: std::future::Future<Output = R> + Send + 'static,
129 R: IntoResponse + 'static,
130{
131 fn call(self, _call: Call) -> HandlerFuture {
132 let f = self;
133 Box::pin(async move { f().await.into_response() })
134 }
135}
136
137// Arity 1 (last arg only) through arity 9 (eight leading parts + last).
138impl_handler!();
139impl_handler!(P1);
140impl_handler!(P1, P2);
141impl_handler!(P1, P2, P3);
142impl_handler!(P1, P2, P3, P4);
143impl_handler!(P1, P2, P3, P4, P5);
144impl_handler!(P1, P2, P3, P4, P5, P6);
145impl_handler!(P1, P2, P3, P4, P5, P6, P7);
146impl_handler!(P1, P2, P3, P4, P5, P6, P7, P8);
147
148/// A type-erased, shared handler — the form the [`Router`](crate::Router)
149/// stores. Create one with [`boxed`].
150pub type BoxHandler = Arc<dyn Handler>;
151
152/// Adapter that wraps a marker-disambiguated [`HandlerFn`] closure into a real
153/// [`Handler`].
154///
155/// This is the bridge that lets the [`boxed`] / router APIs accept extractor
156/// closures: the closure is wrapped here, then boxed. The `Marker` type
157/// parameter only encodes the closure's argument shape (so per-arity impls stay
158/// disjoint) and is erased once wrapped, so it never escapes into
159/// [`BoxHandler`]. You normally obtain one via [`IntoHandler::into_handler`]
160/// rather than naming it directly.
161pub struct HandlerFnAdapter<Marker, H> {
162 handler: H,
163 _marker: std::marker::PhantomData<fn() -> Marker>,
164}
165
166impl<Marker, H> HandlerFnAdapter<Marker, H>
167where
168 H: HandlerFn<Marker>,
169{
170 fn new(handler: H) -> Self {
171 Self {
172 handler,
173 _marker: std::marker::PhantomData,
174 }
175 }
176}
177
178impl<Marker, H> Handler for HandlerFnAdapter<Marker, H>
179where
180 Marker: 'static,
181 H: HandlerFn<Marker>,
182{
183 fn handle(&self, call: Call) -> HandlerFuture {
184 self.handler.clone().call(call)
185 }
186}
187
188/// Bridge from a closure (or anything that is already a `Handler`) into a value
189/// implementing `Handler`, so it can be passed to `boxed` and the router.
190///
191/// Two disjoint impls:
192/// - extractor closures (`HandlerFn<Marker>`) wrap into a `HandlerFnAdapter`;
193/// - anything already implementing `Handler` (including a `BoxHandler`) passes
194/// through unchanged.
195#[diagnostic::on_unimplemented(
196 message = "`{Self}` cannot be used as a route handler",
197 label = "not a valid handler",
198 note = "check each argument: all but the last must implement `FromCallParts`, and only the last may consume the body",
199 note = "a body-consuming extractor (`Json`, `Form`, `Bytes`, `String`, `Payload`, `Multipart`, `Either`) must come last, and there can be only one",
200 note = "`Option<T>` works where `T` implements `OptionalFromCallParts`",
201 note = "the closure's return type must implement `IntoResponse`; `Result<T, E>` works when both `T: IntoResponse` and `E: IntoError`"
202)]
203pub trait IntoHandler<Marker> {
204 /// The concrete [`Handler`] type this value converts into.
205 type Handler: Handler;
206 /// Convert `self` into its [`Handler`] representation.
207 fn into_handler(self) -> Self::Handler;
208}
209
210/// Marker for the "already a `Handler`" pass-through impl, kept distinct from
211/// the closure-arity tuple markers so the two impls never overlap.
212#[doc(hidden)]
213pub struct IsHandler;
214
215// Without this, a closure that fails its bounds is reported against the
216// pass-through impl — "the trait `Handler` is not implemented for
217// `{closure}`" — which sends the reader to implement `Handler` by hand rather
218// than to the argument that is actually wrong.
219#[diagnostic::do_not_recommend]
220impl<H: Handler> IntoHandler<IsHandler> for H {
221 type Handler = H;
222 fn into_handler(self) -> H {
223 self
224 }
225}
226
227/// Closures of any supported arity convert via the adapter. The wrapping marker
228/// keeps this disjoint from the `IsHandler` pass-through (closures do not
229/// implement `Handler` directly, so no closure can match both impls).
230impl<Marker, H> IntoHandler<(IsHandler, Marker)> for H
231where
232 Marker: 'static,
233 H: HandlerFn<Marker>,
234{
235 type Handler = HandlerFnAdapter<Marker, H>;
236 fn into_handler(self) -> HandlerFnAdapter<Marker, H> {
237 HandlerFnAdapter::new(self)
238 }
239}
240
241/// `Arc<dyn Handler>` (i.e. `BoxHandler`) is itself a `Handler`, so a
242/// pre-boxed handler can be re-boxed or passed to the router builders.
243impl Handler for BoxHandler {
244 fn handle(&self, call: Call) -> HandlerFuture {
245 (**self).handle(call)
246 }
247}
248
249/// Box a [`Handler`] into a shareable [`BoxHandler`] for storage in the
250/// [`Router`](crate::Router).
251///
252/// This accepts any `H: Handler`. Extractor closures are not `Handler`s
253/// directly — convert them first with [`IntoHandler::into_handler`] (the router
254/// builders do this for you). A [`BoxHandler`] is itself a `Handler`, so it may
255/// be re-boxed.
256///
257/// ```
258/// use churust_core::{boxed, Call, IntoHandler};
259/// use http::{HeaderMap, Method};
260/// use bytes::Bytes;
261/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
262/// let handler = boxed((|_c: Call| async { "hi" }).into_handler());
263/// let call = Call::new(Method::GET, "/".parse().unwrap(), HeaderMap::new(), Bytes::new());
264/// let res = handler.handle(call).await;
265/// assert_eq!(res.body.as_slice(), Some(&b"hi"[..]));
266/// # });
267/// ```
268pub fn boxed<H: Handler>(h: H) -> BoxHandler {
269 Arc::new(h)
270}
271
272#[cfg(test)]
273mod tests {
274 use super::*;
275 use bytes::Bytes;
276 use http::{HeaderMap, Method, StatusCode, Uri};
277
278 fn sample_call() -> Call {
279 Call::new(
280 Method::GET,
281 "/".parse::<Uri>().unwrap(),
282 HeaderMap::new(),
283 Bytes::new(),
284 )
285 }
286
287 #[tokio::test]
288 async fn closure_returning_str_is_a_handler() {
289 let h = boxed((|_call: Call| async move { "hello" }).into_handler());
290 let res = h.handle(sample_call()).await;
291 assert_eq!(res.status, StatusCode::OK);
292 assert_eq!(res.body, Bytes::from("hello"));
293 }
294
295 #[tokio::test]
296 async fn closure_returning_result_is_a_handler() {
297 let h = boxed(
298 (|call: Call| async move {
299 let _ = call.path();
300 Ok::<_, crate::Error>((StatusCode::CREATED, "made"))
301 })
302 .into_handler(),
303 );
304 let res = h.handle(sample_call()).await;
305 assert_eq!(res.status, StatusCode::CREATED);
306 }
307
308 use crate::extract::FromCallParts;
309
310 struct Greeting(&'static str);
311 #[async_trait::async_trait]
312 impl FromCallParts for Greeting {
313 async fn from_call_parts(_call: &mut Call) -> crate::Result<Self> {
314 Ok(Greeting("hi"))
315 }
316 }
317
318 #[tokio::test]
319 async fn extractor_style_handler_works() {
320 let h = boxed((|g: Greeting| async move { g.0 }).into_handler());
321 let res = h.handle(sample_call()).await;
322 assert_eq!(res.body, bytes::Bytes::from("hi"));
323 }
324
325 // Regression guard for the spec's `boxed<H: Handler>` signature: an explicit
326 // `impl Handler for MyType` must be passable to `boxed()` directly.
327 struct ManualHandler;
328 impl Handler for ManualHandler {
329 fn handle(&self, _call: Call) -> HandlerFuture {
330 Box::pin(async move { "manual".into_response() })
331 }
332 }
333
334 #[tokio::test]
335 async fn manual_impl_handler_is_boxable() {
336 let h = boxed(ManualHandler);
337 let res = h.handle(sample_call()).await;
338 assert_eq!(res.body, Bytes::from("manual"));
339 }
340
341 // Regression guard: a pre-boxed `BoxHandler` is itself a `Handler`, so it can
342 // be re-boxed and (in the router) re-registered.
343 #[tokio::test]
344 async fn box_handler_is_a_handler() {
345 let boxed_once: BoxHandler = boxed(ManualHandler);
346 let boxed_twice = boxed(boxed_once);
347 let res = boxed_twice.handle(sample_call()).await;
348 assert_eq!(res.body, Bytes::from("manual"));
349 }
350}