use crate::http_server::WebServerState;
use crate::http_server_converters::{
axum_request_to_http_request, error_response, http_response_to_axum_response,
};
use crate::http_server_middleware::{execute_middleware_chain, execute_route_handler};
use crate::runtime::engine::Runtime;
use axum::{
body::Body,
extract::{Request, State},
response::Response,
};
use std::collections::HashMap;
use std::ops::Deref;
use std::sync::Arc;
use tokio::sync::RwLock;
pub async fn handle_with_middleware(
State(state): State<Arc<RwLock<WebServerState>>>,
request: Request,
handler_name: &str,
) -> Response<Body> {
let mut http_request = axum_request_to_http_request(request).await;
let state_guard = state.read().await;
if let Some((_, params)) = get_route_handler_name_and_params(
&state_guard.server,
&http_request.method,
&http_request.path,
) {
http_request.path_params = params;
}
let middleware_list = state_guard.server.middleware.clone();
let handler_name = handler_name.to_string();
let runtime_factory = state_guard.runtime_factory.clone();
let scope_writeback = state_guard.scope_writeback.clone();
drop(state_guard);
let response = tokio::task::spawn_blocking(move || {
let mut runtime = if let Some(ref factory) = runtime_factory {
(factory.as_ref().deref())()
} else {
Runtime::new()
};
let processed_request =
match execute_middleware_chain(&mut runtime, &middleware_list, http_request) {
Ok(req) => req,
Err(e) => {
return Err(format!("Middleware error: {}", e));
}
};
let result = execute_route_handler(&mut runtime, &handler_name, processed_request)
.map_err(|e| format!("Handler error: {}", e));
if let Some(ref writeback) = scope_writeback {
writeback(&runtime.scope);
}
result
})
.await
.map_err(|e| format!("Blocking task join error: {}", e));
match response {
Ok(Ok(resp)) => http_response_to_axum_response(resp),
Ok(Err(e)) => error_response(500, &e),
Err(e) => error_response(500, &e),
}
}
fn path_pattern_matches(pattern: &str, path: &str) -> bool {
let pattern_segments: Vec<&str> = pattern.split('/').filter(|s| !s.is_empty()).collect();
let path_segments: Vec<&str> = path.split('/').filter(|s| !s.is_empty()).collect();
if pattern_segments.len() != path_segments.len() {
return false;
}
for (p, v) in pattern_segments.iter().zip(path_segments.iter()) {
if !(*p == *v || (p.starts_with(':') && !v.is_empty())) {
return false;
}
}
true
}
fn path_params_from_match(pattern: &str, path: &str) -> Option<HashMap<String, String>> {
let pattern_segments: Vec<&str> = pattern.split('/').filter(|s| !s.is_empty()).collect();
let path_segments: Vec<&str> = path.split('/').filter(|s| !s.is_empty()).collect();
if pattern_segments.len() != path_segments.len() {
return None;
}
let mut params = HashMap::new();
for (p, v) in pattern_segments.iter().zip(path_segments.iter()) {
if p.starts_with(':') && !v.is_empty() {
let param_name = p.trim_start_matches(':');
params.insert(param_name.to_string(), (*v).to_string());
} else if *p != *v {
return None;
}
}
Some(params)
}
pub fn get_route_handler_name_and_params(
server: &crate::stdlib::web::HttpServer,
method: &str,
path: &str,
) -> Option<(String, HashMap<String, String>)> {
let method_upper = method.to_uppercase();
let route_key = format!("{}:{}", method_upper, path);
if let Some(route) = server.routes.get(&route_key) {
return Some((route.handler.clone(), HashMap::new()));
}
for (key, route) in &server.routes {
if let Some((route_method, route_path)) = key.split_once(':') {
if route_method.eq_ignore_ascii_case(method) && path_pattern_matches(route_path, path) {
let params = path_params_from_match(route_path, path).unwrap_or_default();
return Some((route.handler.clone(), params));
}
}
}
None
}
pub fn get_route_handler_name(
server: &crate::stdlib::web::HttpServer,
method: &str,
path: &str,
) -> Option<String> {
get_route_handler_name_and_params(server, method, path).map(|(name, _)| name)
}