tako_rs_core/handler.rs
1#![allow(non_snake_case)]
2
3//! Request handler traits and implementations for type-safe HTTP processing.
4//!
5//! This module provides the core handler abstraction for Tako applications. Handlers are
6//! asynchronous functions that process HTTP requests and produce responses. The `Handler`
7//! trait enables type-safe request processing with automatic response conversion, while
8//! `BoxHandler` provides type erasure for dynamic handler storage and composition.
9//!
10//! # Examples
11//!
12//! ```rust
13//! use tako::handler::{Handler, BoxHandler};
14//! use tako::types::{Request, Response};
15//! use tako::body::TakoBody;
16//! use std::future::Future;
17//!
18//! // Simple handler function
19//! async fn hello_handler(_req: Request) -> &'static str {
20//! "Hello, World!"
21//! }
22//!
23//! // Handler with custom response type
24//! async fn json_handler(_req: Request) -> Response {
25//! Response::new(TakoBody::from(r#"{"message": "Hello, JSON!"}"#))
26//! }
27//!
28//! // Box handlers for dynamic storage
29//! let boxed = BoxHandler::new(hello_handler);
30//! ```
31
32use std::future::Future;
33use std::pin::Pin;
34use std::sync::Arc;
35
36use futures_util::future::BoxFuture;
37
38use crate::extractors::FromRequest;
39use crate::responder::Responder;
40use crate::types::Request;
41use crate::types::Response;
42
43/// Trait for asynchronous HTTP request handlers.
44///
45/// The `Handler` trait represents functions that process HTTP requests and produce responses.
46/// It is automatically implemented for async functions and closures that take a `Request`
47/// and return any type implementing `Responder`. This enables flexible handler composition
48/// and type-safe response generation throughout the framework.
49///
50/// # Examples
51///
52/// ```rust
53/// use tako::handler::Handler;
54/// use tako::types::{Request, Response};
55/// use tako::responder::Responder;
56/// use http::StatusCode;
57///
58/// // Simple string handler
59/// async fn text_handler(_req: Request) -> &'static str {
60/// "Hello, World!"
61/// }
62///
63/// // Status code with body
64/// async fn status_handler(_req: Request) -> (StatusCode, &'static str) {
65/// (StatusCode::CREATED, "Resource created")
66/// }
67///
68/// // Custom response handler
69/// async fn custom_handler(_req: Request) -> Response {
70/// Response::new(tako::body::TakoBody::from("Custom response"))
71/// }
72/// ```
73pub trait Handler<T>: Send + Sync + 'static {
74 /// Calls the handler with the given request.
75 ///
76 /// Returns an unboxed future — `BoxHandler` is the single boxing point
77 /// for type erasure, eliminating per-handler heap allocation.
78 fn call(self, req: Request) -> impl Future<Output = Response> + Send + 'static;
79}
80
81/// Type-erased handler wrapper for dynamic storage and composition.
82///
83/// Handlers can now be written with or without extractor parameters, similar to Axum.
84/// For example: `async fn handler() -> impl Responder`, `async fn handler(Json<T>) -> _`,
85/// or `async fn handler(Path(p): Path<'_>, Query<Q>) -> _`.
86#[derive(Clone)]
87pub struct BoxHandler {
88 /// The inner function that processes requests and produces responses.
89 inner: Arc<dyn Fn(Request) -> BoxFuture<'static, Response> + Send + Sync>,
90}
91
92impl BoxHandler {
93 /// Creates a new boxed handler from any handler implementation.
94 ///
95 /// This is the single boxing point — `Handler::call` returns an unboxed future
96 /// and `Box::pin` is applied only here for type erasure.
97 pub(crate) fn new<H, T>(h: H) -> Self
98 where
99 H: Handler<T> + Clone,
100 {
101 let inner = Arc::new(move |req: Request| -> BoxFuture<'static, Response> {
102 Box::pin(h.clone().call(req))
103 });
104
105 Self { inner }
106 }
107
108 /// Calls the boxed handler with the provided request.
109 pub(crate) fn call(&self, req: Request) -> BoxFuture<'_, Response> {
110 (self.inner)(req)
111 }
112}
113
114// Zero-argument handlers: `async fn handler() -> impl Responder`
115impl<F, Fut, R> Handler<()> for F
116where
117 F: FnOnce() -> Fut + Clone + Send + Sync + 'static,
118 Fut: Future<Output = R> + Send + 'static,
119 R: Responder,
120{
121 fn call(self, _req: Request) -> impl Future<Output = Response> + Send + 'static {
122 async move { (self)().await.into_response() }
123 }
124}
125
126// Back-compat: single Request arg handlers: `async fn handler(req: Request) -> impl Responder`
127impl<F, Fut, R> Handler<(Request,)> for F
128where
129 F: FnOnce(Request) -> Fut + Clone + Send + Sync + 'static,
130 Fut: Future<Output = R> + Send + 'static,
131 R: Responder,
132{
133 fn call(self, req: Request) -> impl Future<Output = Response> + Send + 'static {
134 async move { (self)(req).await.into_response() }
135 }
136}
137
138// Abstraction over extraction that avoids HRTB bounds in impls.
139trait Extract: Sized + Send {
140 type Error: Responder;
141
142 fn extract<'a>(
143 req: &'a mut Request,
144 ) -> Pin<Box<dyn Future<Output = core::result::Result<Self, Self::Error>> + Send + 'a>>;
145}
146
147impl<T, E> Extract for T
148where
149 T: Send,
150 E: Responder,
151 for<'a> T: FromRequest<'a, Error = E>,
152{
153 type Error = E;
154
155 fn extract<'a>(
156 req: &'a mut Request,
157 ) -> Pin<Box<dyn Future<Output = core::result::Result<Self, Self::Error>> + Send + 'a>> {
158 Box::pin(<T as FromRequest<'a>>::from_request(req))
159 }
160}
161
162macro_rules! impl_handler {
163 ($($T:ident),+ $(,)?) => {
164 impl<Func, Fut, R, $($T,)*> Handler<($($T,)*)> for Func
165 where
166 Func: FnOnce($($T),*) -> Fut + Clone + Send + Sync + 'static,
167 Fut: Future<Output = R> + Send + 'static,
168 R: Responder,
169 $( $T: Extract + Send, )*
170 {
171 fn call(self, mut req: Request) -> impl Future<Output = Response> + Send + 'static {
172 async move {
173 $(
174 let $T = match <$T as Extract>::extract(&mut req).await {
175 Ok(value) => value,
176 Err(err) => {
177 return err.into_response();
178 }
179 };
180 )*
181 (self)($($T),*).await.into_response()
182 }
183 }
184 }
185 };
186}
187
188impl_handler!(T1);
189impl_handler!(T1, T2);
190impl_handler!(T1, T2, T3);
191impl_handler!(T1, T2, T3, T4);
192impl_handler!(T1, T2, T3, T4, T5);
193impl_handler!(T1, T2, T3, T4, T5, T6);
194impl_handler!(T1, T2, T3, T4, T5, T6, T7);
195impl_handler!(T1, T2, T3, T4, T5, T6, T7, T8);
196impl_handler!(T1, T2, T3, T4, T5, T6, T7, T8, T9);
197impl_handler!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
198impl_handler!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
199impl_handler!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);