Skip to main content

httpward_core/
middleware_config_ext.rs

1use crate::config::strategy::MiddlewareConfig;
2use crate::core::HttpWardContext;
3use rama::Context;
4use rama::http::{Request, Body};
5use serde::de::DeserializeOwned;
6use std::error::Error;
7
8/// Convenient trait for getting middleware configuration from context
9pub trait MiddlewareConfigExt {
10    /// Get middleware configuration for current route by name
11    fn get_middleware_config(&self, middleware_name: &str) -> Result<Option<MiddlewareConfig>, Box<dyn Error + Send + Sync>>;
12    
13    /// Get and parse middleware configuration into specific type
14    fn get_middleware_config_typed<T>(&self, middleware_name: &str) -> Result<Option<T>, Box<dyn Error + Send + Sync>>
15    where
16        T: DeserializeOwned;
17    
18    /// Get middleware configuration by name and path
19    fn get_middleware_config_for_path(&self, path: &str, middleware_name: &str) -> Result<Option<MiddlewareConfig>, Box<dyn Error + Send + Sync>>;
20    
21    /// Get and parse middleware configuration for specific path
22    fn get_middleware_config_for_path_typed<T>(&self, path: &str, middleware_name: &str) -> Result<Option<T>, Box<dyn Error + Send + Sync>>
23    where
24        T: DeserializeOwned;
25}
26
27impl MiddlewareConfigExt for Context<()> {
28    fn get_middleware_config(&self, middleware_name: &str) -> Result<Option<MiddlewareConfig>, Box<dyn Error + Send + Sync>> {
29        let httpward_ctx = self.get::<HttpWardContext>()
30            .ok_or("HttpWardContext not found")?;
31        
32        // Get path from request or use current route
33        if let Some((path, _)) = self.get::<(String, ())>() {
34            // If path is saved in context, use it
35            if let Some(site) = &httpward_ctx.current_site {
36                site.get_active_strategy_config_by_route(&path, middleware_name)
37                    .map_err(|e| Box::new(e) as Box<dyn Error + Send + Sync>)
38            } else {
39                Err("No current site".into())
40            }
41        } else {
42            // Otherwise try to get from current route
43            Err("No path information available in context".into())
44        }
45    }
46    
47    fn get_middleware_config_typed<T>(&self, middleware_name: &str) -> Result<Option<T>, Box<dyn Error + Send + Sync>>
48    where
49        T: DeserializeOwned,
50    {
51        if let Some(config) = self.get_middleware_config(middleware_name)? {
52            let typed_config: T = config.config_into()?;
53            Ok(Some(typed_config))
54        } else {
55            Ok(None)
56        }
57    }
58    
59    fn get_middleware_config_for_path(&self, path: &str, middleware_name: &str) -> Result<Option<MiddlewareConfig>, Box<dyn Error + Send + Sync>> {
60        let httpward_ctx = self.get::<HttpWardContext>()
61            .ok_or("HttpWardContext not found")?;
62        
63        if let Some(site) = &httpward_ctx.current_site {
64            site.get_active_strategy_config_by_route(path, middleware_name)
65                .map_err(|e| Box::new(e) as Box<dyn Error + Send + Sync>)
66        } else {
67            Err("No current site".into())
68        }
69    }
70    
71    fn get_middleware_config_for_path_typed<T>(&self, path: &str, middleware_name: &str) -> Result<Option<T>, Box<dyn Error + Send + Sync>>
72    where
73        T: DeserializeOwned,
74    {
75        if let Some(config) = self.get_middleware_config_for_path(path, middleware_name)? {
76            let typed_config: T = config.config_into()?;
77            Ok(Some(typed_config))
78        } else {
79            Ok(None)
80        }
81    }
82}
83
84/// Universal function to get middleware configuration from context
85/// Usage: get_config_from_ctx::<MyConfig>(ctx, req, "my_middleware_name")
86pub fn get_config_from_ctx<T>(
87    ctx: &Context<()>,
88    req: &Request<Body>,
89    middleware_name: &str,
90) -> Result<T, Box<dyn Error + Send + Sync>>
91where
92    T: DeserializeOwned,
93{
94    let path = req.uri().path();
95    ctx.get_middleware_config_for_path_typed::<T>(path, middleware_name)?
96        .ok_or_else(|| format!("No configuration found for middleware '{}' on path '{}'", middleware_name, path).into())
97}
98
99/// Universal function to get middleware configuration from context with module name
100/// Usage: get_config_from_ctx_for_module::<MyConfig>(ctx, req, "my_module_name")
101pub fn get_config_from_ctx_for_module<T>(
102    ctx: &Context<()>,
103    req: &Request<Body>,
104    module_name: &str,
105) -> Result<T, Box<dyn Error + Send + Sync>>
106where
107    T: DeserializeOwned,
108{
109    get_config_from_ctx::<T>(ctx, req, module_name)
110}
111
112/// Macro to get config using current module name automatically
113/// Usage: get_config_from_current_module!(MyConfig, ctx, req)
114#[macro_export]
115macro_rules! get_config_from_current_module {
116    ($config_type:ty, $ctx:expr, $req:expr) => {
117        $crate::middleware_config_ext::get_config_from_ctx_for_module::<$config_type>(
118            $ctx,
119            $req,
120            module_path!().split("::").last().unwrap_or("unknown")
121        )
122    };
123}
124
125/// Macro to get config using current crate name automatically
126/// Usage: get_module_config_from_current_crate!(MyConfig, ctx, req)
127#[macro_export]
128macro_rules! get_module_config_from_current_crate {
129    ($config_type:ty, $ctx:expr, $req:expr) => {
130        $crate::middleware_config_ext::get_config_from_ctx_for_module::<$config_type>(
131            $ctx,
132            $req,
133            env!("CARGO_PKG_NAME")
134        )
135    };
136}
137
138/// Convenient function for getting middleware configuration from context
139pub fn get_middleware_config_from_ctx(
140    ctx: &Context<()>,
141    middleware_name: &str,
142) -> Result<Option<MiddlewareConfig>, Box<dyn Error + Send + Sync>> {
143    ctx.get_middleware_config(middleware_name)
144}
145
146/// Convenient function for getting typed configuration
147pub fn get_middleware_config_typed_from_ctx<T>(
148    ctx: &Context<()>,
149    middleware_name: &str,
150) -> Result<Option<T>, Box<dyn Error + Send + Sync>>
151where
152    T: DeserializeOwned,
153{
154    ctx.get_middleware_config_typed(middleware_name)
155}