tako_rs_core/middleware.rs
1//! Middleware system for request and response processing pipelines.
2//!
3//! This module provides the core middleware infrastructure for Tako, allowing you to
4//! compose request processing pipelines. Middleware can modify requests, responses,
5//! or perform side effects like logging, authentication, or rate limiting. The `Next`
6//! struct manages the execution flow through the middleware chain to the final handler.
7//!
8//! # Examples
9//!
10//! ```rust
11//! use tako::{middleware::Next, types::{Request, Response}};
12//! use std::{pin::Pin, future::Future};
13//!
14//! async fn middleware(req: Request, next: Next) -> Response {
15//! // Your logic here
16//! next.run(req).await
17//! }
18//! ```
19
20use std::future::Future;
21use std::pin::Pin;
22use std::sync::Arc;
23
24use crate::handler::BoxHandler;
25use crate::types::BoxMiddleware;
26use crate::types::Request;
27use crate::types::Response;
28
29/// Trait for converting types into middleware functions.
30///
31/// This trait allows various types to be converted into middleware that can be used
32/// in the Tako middleware pipeline. Middleware functions take a request and the next
33/// middleware in the chain, returning a future that resolves to a response.
34///
35/// # Examples
36///
37/// ```rust
38/// use tako::middleware::{IntoMiddleware, Next};
39/// use tako::types::{Request, Response};
40/// use std::{pin::Pin, future::Future};
41///
42/// struct LoggingMiddleware;
43///
44/// impl IntoMiddleware for LoggingMiddleware {
45/// fn into_middleware(
46/// self,
47/// ) -> impl Fn(Request, Next) -> Pin<Box<dyn Future<Output = Response> + Send + 'static>>
48/// + Clone + Send + Sync + 'static {
49/// |req, next| {
50/// Box::pin(async move {
51/// println!("Request: {}", req.uri());
52/// next.run(req).await
53/// })
54/// }
55/// }
56/// }
57/// ```
58#[doc(alias = "middleware")]
59pub trait IntoMiddleware {
60 fn into_middleware(
61 self,
62 ) -> impl Fn(Request, Next) -> Pin<Box<dyn Future<Output = Response> + Send + 'static>>
63 + Clone
64 + Send
65 + Sync
66 + 'static;
67}
68
69/// Represents the next step in the middleware execution chain.
70///
71/// `Next` is passed to middleware functions to allow them to continue
72/// the request processing chain. Calling `next.run(req)` will execute
73/// the remaining middleware and eventually the endpoint handler.
74#[doc(alias = "next")]
75pub struct Next {
76 /// Global middlewares to be executed before route-specific ones.
77 pub global_middlewares: Arc<Vec<BoxMiddleware>>,
78 /// Route-specific middlewares executed after global ones.
79 pub route_middlewares: Arc<Vec<BoxMiddleware>>,
80 /// Current position within the middleware chain.
81 pub index: usize,
82 /// Final endpoint handler to be called after all middlewares.
83 pub endpoint: BoxHandler,
84}
85
86impl std::fmt::Debug for Next {
87 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88 f.debug_struct("Next")
89 .field(
90 "middlewares_remaining",
91 &(self.global_middlewares.len() + self.route_middlewares.len()).saturating_sub(self.index),
92 )
93 .finish_non_exhaustive()
94 }
95}
96
97impl Clone for Next {
98 fn clone(&self) -> Self {
99 Self {
100 global_middlewares: Arc::clone(&self.global_middlewares),
101 route_middlewares: Arc::clone(&self.route_middlewares),
102 index: self.index,
103 endpoint: self.endpoint.clone(),
104 }
105 }
106}
107
108impl Next {
109 /// Executes the next middleware or endpoint in the chain.
110 pub async fn run(mut self, req: Request) -> Response {
111 let mw = if let Some(mw) = self.global_middlewares.get(self.index) {
112 Some(mw.clone())
113 } else {
114 self
115 .route_middlewares
116 .get(self.index.saturating_sub(self.global_middlewares.len()))
117 .cloned()
118 };
119
120 if let Some(mw) = mw {
121 self.index += 1;
122 mw(req, self).await
123 } else {
124 self.endpoint.call(req).await
125 }
126 }
127}