Skip to main content

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);