awpak_rs/services/middleware/
middleware.rs

1use std::sync::OnceLock;
2
3use crate::io::io::IO;
4
5/// Represents the possible outcomes of a middleware execution.
6///
7/// A middleware can either allow the request to continue processing (`Next`)
8/// or stop further processing and return a response immediately (`Cancel`).
9pub enum MiddlewareResponse
10{
11    /// Continues to the next middleware or the endpoint if no more middlewares are left.
12    ///
13    /// The `IO` instance passed will be forwarded to the next middleware or the endpoint.
14    Next( IO ),
15
16    /// Cancels the request processing and immediately returns a response to the client.
17    ///
18    /// The `IO` instance provided will be used as the final response.
19    Cancel( IO )
20}
21
22pub type MiddlewareResponseType = std::pin::Pin<std::boxed::Box<
23                                    dyn std::future::Future<
24                                        Output = MiddlewareResponse
25                                    > 
26                                    + std::marker::Send
27                                >>;
28
29#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
30pub enum MiddlewareExecOrder
31{
32    PRE,
33    POST
34}
35
36#[derive(Copy, Clone)]
37pub struct Middleware
38{
39    pub regex : Option<&'static str>,
40    pub order : usize,
41    pub method : Option<&'static str>,
42    pub fnc : fn( IO ) -> MiddlewareResponseType,
43    pub exec_order : MiddlewareExecOrder
44}
45
46impl Middleware
47{
48    pub const fn new(
49        regex : Option<&'static str>,
50        order : usize,
51        method : Option<&'static str>,
52        fnc : fn( IO ) -> MiddlewareResponseType,
53        exec_order : MiddlewareExecOrder
54    ) -> Self
55    {
56        Self
57        {
58            regex,
59            order,
60            method,
61            fnc,
62            exec_order
63        }
64    }
65}
66
67inventory::collect!( Middleware );
68
69fn pre_middlewares() -> &'static Vec<Middleware> {
70    static ARRAY_PRE_MIDDLEWARES: OnceLock<Vec<Middleware>> = OnceLock::new();
71    ARRAY_PRE_MIDDLEWARES.get_or_init(|| get_init_middlewares( MiddlewareExecOrder::PRE ) )
72}
73
74fn post_middlewares() -> &'static Vec<Middleware> {
75    static ARRAY_POST_MIDDLEWARES: OnceLock<Vec<Middleware>> = OnceLock::new();
76    ARRAY_POST_MIDDLEWARES.get_or_init(|| get_init_middlewares( MiddlewareExecOrder::POST ) )
77}
78
79pub fn initialize_middlewares()
80{
81    let _ = pre_middlewares();
82    let _ = post_middlewares();
83}
84
85fn get_init_middlewares( exec_order : MiddlewareExecOrder ) -> Vec<Middleware>
86{
87    let mut middlewares : Vec<Middleware> = vec![];
88    
89    for m in inventory::iter::<Middleware>
90    {
91        if m.exec_order == exec_order
92        {
93            middlewares.push( *m );
94        }
95    }
96
97    middlewares.sort_by( | a, b | a.order.cmp( &b.order ) );
98
99    middlewares
100}
101
102pub async fn pre_middlewares_exec( io : IO ) -> MiddlewareResponse
103{
104    exec_middlewares( io, MiddlewareExecOrder::PRE ).await
105}
106
107pub async fn post_middlewares_exec( io : IO ) -> MiddlewareResponse
108{
109    exec_middlewares( io, MiddlewareExecOrder::POST ).await
110}
111
112async fn exec_middlewares( mut io : IO, exec_order : MiddlewareExecOrder ) -> MiddlewareResponse
113{
114    let middlewares = match exec_order
115    {
116        MiddlewareExecOrder::POST => post_middlewares(),
117        MiddlewareExecOrder::PRE => pre_middlewares()
118    };
119
120    for middleware in middlewares
121    {
122        if middleware.method.is_some()
123        {
124            if &io.request.method.to_lowercase().as_str() != middleware.method.as_ref().unwrap()
125            {
126                continue;
127            }
128        }
129
130        if middleware.regex.is_some()
131        {
132            match regex::Regex::new( middleware.regex.as_ref().unwrap() ) {
133                
134                Ok( v ) => 
135                    if v.is_match( &io.request.uri.path ) {} else { continue; },
136                _ => { continue; }
137            };
138        }
139
140        io =  match ( middleware.fnc )( io ).await {
141            MiddlewareResponse::Next( v ) => v,
142            MiddlewareResponse::Cancel( v ) => return MiddlewareResponse::Cancel( v )
143        };
144    }
145
146    MiddlewareResponse::Next( io )
147}