Skip to main content

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}