1use crate::extract::{FromRequest, RequestCtx};
6use crate::response::{IntoResponse, Response};
7use std::future::Future;
8use std::pin::Pin;
9use std::sync::Arc;
10
11pub(crate) type BoxHandlerFn = Arc<
13 dyn for<'a> Fn(&'a mut RequestCtx) -> Pin<Box<dyn Future<Output = Response> + Send + 'a>>
14 + Send
15 + Sync,
16>;
17
18pub trait Handler<Args>: Send + Sync + 'static {
20 #[doc(hidden)]
21 fn into_handler_fn(self) -> BoxHandlerFn;
22}
23
24macro_rules! impl_handler {
25 ($($A:ident),*) => {
26 impl<F, Fut, R, $($A,)*> Handler<($($A,)*)> for F
27 where
28 F: Fn($($A),*) -> Fut + Clone + Send + Sync + 'static,
29 Fut: Future<Output = R> + Send,
30 R: IntoResponse,
31 $($A: FromRequest + 'static,)*
32 {
33 fn into_handler_fn(self) -> BoxHandlerFn {
34 #[allow(unused_variables)]
35 Arc::new(move |ctx: &mut RequestCtx| {
36 let f = self.clone();
37 Box::pin(async move {
38 #[allow(non_snake_case, unused_variables)]
39 {
40 $(
41 let $A = match <$A as FromRequest>::from_request(ctx).await {
42 Ok(v) => v,
43 Err(e) => return e.into_response(),
44 };
45 )*
46 f($($A),*).await.into_response()
47 }
48 })
49 })
50 }
51 }
52 };
53}
54
55impl_handler!();
56impl_handler!(A1);
57impl_handler!(A1, A2);
58impl_handler!(A1, A2, A3);
59impl_handler!(A1, A2, A3, A4);
60impl_handler!(A1, A2, A3, A4, A5);
61impl_handler!(A1, A2, A3, A4, A5, A6);
62impl_handler!(A1, A2, A3, A4, A5, A6, A7);
63impl_handler!(A1, A2, A3, A4, A5, A6, A7, A8);
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68 use crate::Path;
69 use crate::dep::{Dep, DepEnv, DepResolver};
70 use crate::response::Json;
71 use std::collections::HashMap;
72
73 struct Greeting {
74 word: &'static str,
75 }
76
77 async fn greet(g: Dep<Greeting>, Path(id): Path<u32>) -> crate::Result<Json<String>> {
78 Ok(Json(format!("{} #{id}", g.word)))
79 }
80
81 #[tokio::test]
82 async fn handler_extracts_runs_and_responds() {
83 let mut env = DepEnv::default();
84 env.insert_value(Greeting { word: "hi" });
85 let req = http::Request::builder().uri("/greet/7").body(()).unwrap();
86 let (parts, ()) = req.into_parts();
87 let mut ctx = RequestCtx::new(
88 parts,
89 bytes::Bytes::new(),
90 DepResolver::new(
91 std::sync::Arc::new(env),
92 std::sync::Arc::new(HashMap::new()),
93 ),
94 );
95 ctx.params.push(("id".into(), "7".into()));
96
97 let h = greet.into_handler_fn();
98 let res = (*h)(&mut ctx).await; assert_eq!(res.status(), http::StatusCode::OK);
100 }
101
102 #[tokio::test]
103 async fn extraction_failure_short_circuits_to_error_response() {
104 let req = http::Request::builder().uri("/greet/7").body(()).unwrap();
106 let (parts, ()) = req.into_parts();
107 let mut ctx = RequestCtx::new(
108 parts,
109 bytes::Bytes::new(),
110 DepResolver::new(Default::default(), std::sync::Arc::new(HashMap::new())),
111 );
112 ctx.params.push(("id".into(), "7".into()));
113
114 let h = greet.into_handler_fn();
115 let res = (*h)(&mut ctx).await;
116 assert_eq!(res.status(), http::StatusCode::INTERNAL_SERVER_ERROR);
117 }
118}