use std::collections::HashMap;
use once_cell::sync::Lazy;
use std::sync::Mutex;
pub static ROUTE_REGISTRY: Lazy<Mutex<HashMap<String, RouteConfig>>> =
Lazy::new(|| Mutex::new(HashMap::new()));
#[derive(Debug, Clone)]
pub struct RouteConfig {
pub path: String,
pub method: String,
pub handler_name: String,
}
pub struct Route;
impl Route {
pub fn register(name: &str, path: &str, method: &str) {
let config = RouteConfig {
path: path.to_string(),
method: method.to_string(),
handler_name: name.to_string(),
};
let key = format!("{}:{}", method, path);
if let Ok(mut registry) = ROUTE_REGISTRY.lock() {
registry.insert(key, config);
}
tracing::debug!("Registered route: {} {} -> {}", method, path, name);
}
pub fn all_routes() -> Vec<RouteConfig> {
ROUTE_REGISTRY
.lock()
.map(|r| r.values().cloned().collect())
.unwrap_or_default()
}
pub fn find_routes(path: &str) -> Vec<RouteConfig> {
ROUTE_REGISTRY
.lock()
.map(|r| {
r.values()
.filter(|c| c.path == path)
.cloned()
.collect()
})
.unwrap_or_default()
}
pub fn by_method(method: &str) -> Vec<RouteConfig> {
ROUTE_REGISTRY
.lock()
.map(|r| {
r.values()
.filter(|c| c.method.eq_ignore_ascii_case(method))
.cloned()
.collect()
})
.unwrap_or_default()
}
#[cfg(test)]
pub fn clear() {
if let Ok(mut r) = ROUTE_REGISTRY.lock() {
r.clear();
}
}
}
pub struct RouteScope {
prefix: String,
}
impl RouteScope {
pub fn new(prefix: &str) -> Self {
Self {
prefix: prefix.trim_end_matches('/').to_string(),
}
}
pub fn join(&self, path: &str) -> String {
if path.starts_with('/') {
format!("{}{}", self.prefix, path)
} else {
format!("{}/{}", self.prefix, path)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_route_register() {
Route::clear();
Route::register("handler1", "/api/users", "GET");
Route::register("handler2", "/api/users", "POST");
let routes = Route::all_routes();
assert_eq!(routes.len(), 2);
}
#[test]
fn test_route_scope() {
let scope = RouteScope::new("/api/v1");
assert_eq!(scope.join("/users"), "/api/v1/users");
assert_eq!(scope.join("users"), "/api/v1/users");
}
}