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.
72pub trait HandlerFn<Marker>: Clone + Send + Sync + 'static {
73 /// Run the closure against the call: extract each argument from the call in
74 /// order, then invoke the closure and convert its return value into a
75 /// [`Response`].
76 fn call(self, call: Call) -> HandlerFuture;
77}
78
79/// Generates a `HandlerFn` impl whose closure args are `($($P,)* L)` — the
80/// `$P` are the leading `FromCallParts` extractors and `L` is the final
81/// `FromCall` extractor. `impl_handler!()` (empty) produces the arity-1
82/// (last-arg-only) impl; the arity-0 impl is written out separately below.
83macro_rules! impl_handler {
84 ($($P:ident),*) => {
85 #[allow(non_snake_case, unused_mut, unused_variables)]
86 impl<F, Fut, R, $($P,)* L> HandlerFn<($($P,)* L,)> for F
87 where
88 F: Fn($($P,)* L) -> Fut + Clone + Send + Sync + 'static,
89 Fut: std::future::Future<Output = R> + Send + 'static,
90 R: IntoResponse + 'static,
91 $($P: FromCallParts + 'static,)*
92 L: FromCall + 'static,
93 {
94 fn call(self, call: Call) -> HandlerFuture {
95 let f = self;
96 Box::pin(async move {
97 let mut call = call;
98 $(
99 let $P = match <$P as FromCallParts>::from_call_parts(&mut call).await {
100 Ok(v) => v,
101 Err(e) => return e.into_response(),
102 };
103 )*
104 let last = match <L as FromCall>::from_call(call).await {
105 Ok(v) => v,
106 Err(e) => return e.into_response(),
107 };
108 f($($P,)* last).await.into_response()
109 })
110 }
111 }
112 };
113}
114
115// Arity 0: a parameterless closure.
116impl<F, Fut, R> HandlerFn<()> for F
117where
118 F: Fn() -> Fut + Clone + Send + Sync + 'static,
119 Fut: std::future::Future<Output = R> + Send + 'static,
120 R: IntoResponse + 'static,
121{
122 fn call(self, _call: Call) -> HandlerFuture {
123 let f = self;
124 Box::pin(async move { f().await.into_response() })
125 }
126}
127
128// Arity 1 (last arg only) through arity 9 (eight leading parts + last).
129impl_handler!();
130impl_handler!(P1);
131impl_handler!(P1, P2);
132impl_handler!(P1, P2, P3);
133impl_handler!(P1, P2, P3, P4);
134impl_handler!(P1, P2, P3, P4, P5);
135impl_handler!(P1, P2, P3, P4, P5, P6);
136impl_handler!(P1, P2, P3, P4, P5, P6, P7);
137impl_handler!(P1, P2, P3, P4, P5, P6, P7, P8);
138
139/// A type-erased, shared handler — the form the [`Router`](crate::Router)
140/// stores. Create one with [`boxed`].
141pub type BoxHandler = Arc<dyn Handler>;
142
143/// Adapter that wraps a marker-disambiguated [`HandlerFn`] closure into a real
144/// [`Handler`].
145///
146/// This is the bridge that lets the [`boxed`] / router APIs accept extractor
147/// closures: the closure is wrapped here, then boxed. The `Marker` type
148/// parameter only encodes the closure's argument shape (so per-arity impls stay
149/// disjoint) and is erased once wrapped, so it never escapes into
150/// [`BoxHandler`]. You normally obtain one via [`IntoHandler::into_handler`]
151/// rather than naming it directly.
152pub struct HandlerFnAdapter<Marker, H> {
153 handler: H,
154 _marker: std::marker::PhantomData<fn() -> Marker>,
155}
156
157impl<Marker, H> HandlerFnAdapter<Marker, H>
158where
159 H: HandlerFn<Marker>,
160{
161 fn new(handler: H) -> Self {
162 Self {
163 handler,
164 _marker: std::marker::PhantomData,
165 }
166 }
167}
168
169impl<Marker, H> Handler for HandlerFnAdapter<Marker, H>
170where
171 Marker: Send + Sync + 'static,
172 H: HandlerFn<Marker>,
173{
174 fn handle(&self, call: Call) -> HandlerFuture {
175 self.handler.clone().call(call)
176 }
177}
178
179/// Bridge from a closure (or anything that is already a `Handler`) into a value
180/// implementing `Handler`, so it can be passed to `boxed` and the router.
181///
182/// Two disjoint impls:
183/// - extractor closures (`HandlerFn<Marker>`) wrap into a `HandlerFnAdapter`;
184/// - anything already implementing `Handler` (including a `BoxHandler`) passes
185/// through unchanged.
186pub trait IntoHandler<Marker> {
187 /// The concrete [`Handler`] type this value converts into.
188 type Handler: Handler;
189 /// Convert `self` into its [`Handler`] representation.
190 fn into_handler(self) -> Self::Handler;
191}
192
193/// Marker for the "already a `Handler`" pass-through impl, kept distinct from
194/// the closure-arity tuple markers so the two impls never overlap.
195#[doc(hidden)]
196pub struct IsHandler;
197
198impl<H: Handler> IntoHandler<IsHandler> for H {
199 type Handler = H;
200 fn into_handler(self) -> H {
201 self
202 }
203}
204
205/// Closures of any supported arity convert via the adapter. The wrapping marker
206/// keeps this disjoint from the `IsHandler` pass-through (closures do not
207/// implement `Handler` directly, so no closure can match both impls).
208impl<Marker, H> IntoHandler<(IsHandler, Marker)> for H
209where
210 Marker: Send + Sync + 'static,
211 H: HandlerFn<Marker>,
212{
213 type Handler = HandlerFnAdapter<Marker, H>;
214 fn into_handler(self) -> HandlerFnAdapter<Marker, H> {
215 HandlerFnAdapter::new(self)
216 }
217}
218
219/// `Arc<dyn Handler>` (i.e. `BoxHandler`) is itself a `Handler`, so a
220/// pre-boxed handler can be re-boxed or passed to the router builders.
221impl Handler for BoxHandler {
222 fn handle(&self, call: Call) -> HandlerFuture {
223 (**self).handle(call)
224 }
225}
226
227/// Box a [`Handler`] into a shareable [`BoxHandler`] for storage in the
228/// [`Router`](crate::Router).
229///
230/// This accepts any `H: Handler`. Extractor closures are not `Handler`s
231/// directly — convert them first with [`IntoHandler::into_handler`] (the router
232/// builders do this for you). A [`BoxHandler`] is itself a `Handler`, so it may
233/// be re-boxed.
234///
235/// ```
236/// use churust_core::{boxed, Call, IntoHandler};
237/// use http::{HeaderMap, Method};
238/// use bytes::Bytes;
239/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
240/// let handler = boxed((|_c: Call| async { "hi" }).into_handler());
241/// let call = Call::new(Method::GET, "/".parse().unwrap(), HeaderMap::new(), Bytes::new());
242/// let res = handler.handle(call).await;
243/// assert_eq!(res.body.as_slice(), Some(&b"hi"[..]));
244/// # });
245/// ```
246pub fn boxed<H: Handler>(h: H) -> BoxHandler {
247 Arc::new(h)
248}
249
250#[cfg(test)]
251mod tests {
252 use super::*;
253 use bytes::Bytes;
254 use http::{HeaderMap, Method, StatusCode, Uri};
255
256 fn sample_call() -> Call {
257 Call::new(
258 Method::GET,
259 "/".parse::<Uri>().unwrap(),
260 HeaderMap::new(),
261 Bytes::new(),
262 )
263 }
264
265 #[tokio::test]
266 async fn closure_returning_str_is_a_handler() {
267 let h = boxed((|_call: Call| async move { "hello" }).into_handler());
268 let res = h.handle(sample_call()).await;
269 assert_eq!(res.status, StatusCode::OK);
270 assert_eq!(res.body, Bytes::from("hello"));
271 }
272
273 #[tokio::test]
274 async fn closure_returning_result_is_a_handler() {
275 let h = boxed(
276 (|call: Call| async move {
277 let _ = call.path();
278 Ok::<_, crate::Error>((StatusCode::CREATED, "made"))
279 })
280 .into_handler(),
281 );
282 let res = h.handle(sample_call()).await;
283 assert_eq!(res.status, StatusCode::CREATED);
284 }
285
286 use crate::extract::FromCallParts;
287
288 struct Greeting(&'static str);
289 #[async_trait::async_trait]
290 impl FromCallParts for Greeting {
291 async fn from_call_parts(_call: &mut Call) -> crate::Result<Self> {
292 Ok(Greeting("hi"))
293 }
294 }
295
296 #[tokio::test]
297 async fn extractor_style_handler_works() {
298 let h = boxed((|g: Greeting| async move { g.0 }).into_handler());
299 let res = h.handle(sample_call()).await;
300 assert_eq!(res.body, bytes::Bytes::from("hi"));
301 }
302
303 // Regression guard for the spec's `boxed<H: Handler>` signature: an explicit
304 // `impl Handler for MyType` must be passable to `boxed()` directly.
305 struct ManualHandler;
306 impl Handler for ManualHandler {
307 fn handle(&self, _call: Call) -> HandlerFuture {
308 Box::pin(async move { "manual".into_response() })
309 }
310 }
311
312 #[tokio::test]
313 async fn manual_impl_handler_is_boxable() {
314 let h = boxed(ManualHandler);
315 let res = h.handle(sample_call()).await;
316 assert_eq!(res.body, Bytes::from("manual"));
317 }
318
319 // Regression guard: a pre-boxed `BoxHandler` is itself a `Handler`, so it can
320 // be re-boxed and (in the router) re-registered.
321 #[tokio::test]
322 async fn box_handler_is_a_handler() {
323 let boxed_once: BoxHandler = boxed(ManualHandler);
324 let boxed_twice = boxed(boxed_once);
325 let res = boxed_twice.handle(sample_call()).await;
326 assert_eq!(res.body, Bytes::from("manual"));
327 }
328}