Skip to main content

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}