use crate::http::{Request, Response};
use crate::middleware::{into_boxed, BoxedMiddleware, Middleware};
use matchit::Router as MatchitRouter;
use serde::Serialize;
use std::collections::HashMap;
use std::future::Future;
use std::pin::Pin;
use std::sync::{Arc, OnceLock, RwLock};
static ROUTE_REGISTRY: OnceLock<RwLock<HashMap<String, String>>> = OnceLock::new();
static REGISTERED_ROUTES: OnceLock<RwLock<Vec<RouteInfo>>> = OnceLock::new();
#[derive(Debug, Clone, Default, Serialize)]
pub struct RouteInfo {
pub method: String,
pub path: String,
pub name: Option<String>,
pub middleware: Vec<String>,
pub mcp_tool_name: Option<String>,
pub mcp_description: Option<String>,
pub mcp_hint: Option<String>,
pub mcp_hidden: bool,
}
fn register_route(method: &str, path: &str) {
let registry = REGISTERED_ROUTES.get_or_init(|| RwLock::new(Vec::new()));
if let Ok(mut routes) = registry.write() {
routes.push(RouteInfo {
method: method.to_string(),
path: path.to_string(),
name: None,
middleware: Vec::new(),
mcp_tool_name: None,
mcp_description: None,
mcp_hint: None,
mcp_hidden: false,
});
}
}
fn update_route_name(path: &str, name: &str) {
let registry = REGISTERED_ROUTES.get_or_init(|| RwLock::new(Vec::new()));
if let Ok(mut routes) = registry.write() {
if let Some(route) = routes.iter_mut().rev().find(|r| r.path == path) {
route.name = Some(name.to_string());
}
}
}
fn update_route_middleware(path: &str, middleware_name: &str) {
let registry = REGISTERED_ROUTES.get_or_init(|| RwLock::new(Vec::new()));
if let Ok(mut routes) = registry.write() {
if let Some(route) = routes.iter_mut().rev().find(|r| r.path == path) {
route.middleware.push(middleware_name.to_string());
}
}
}
pub(crate) fn update_route_mcp(
path: &str,
tool_name: Option<String>,
description: Option<String>,
hint: Option<String>,
hidden: bool,
) {
let registry = REGISTERED_ROUTES.get_or_init(|| RwLock::new(Vec::new()));
if let Ok(mut routes) = registry.write() {
if let Some(route) = routes.iter_mut().rev().find(|r| r.path == path) {
route.mcp_tool_name = tool_name;
route.mcp_description = description;
route.mcp_hint = hint;
route.mcp_hidden = hidden;
}
}
}
pub fn get_registered_routes() -> Vec<RouteInfo> {
REGISTERED_ROUTES
.get()
.and_then(|r| r.read().ok())
.map(|routes| routes.clone())
.unwrap_or_default()
}
pub fn register_route_name(name: &str, path: &str) {
let registry = ROUTE_REGISTRY.get_or_init(|| RwLock::new(HashMap::new()));
if let Ok(mut map) = registry.write() {
map.insert(name.to_string(), path.to_string());
}
update_route_name(path, name);
}
pub fn route(name: &str, params: &[(&str, &str)]) -> Option<String> {
let registry = ROUTE_REGISTRY.get()?.read().ok()?;
let path_pattern = registry.get(name)?;
let mut url = path_pattern.clone();
for (key, value) in params {
url = url.replace(&format!("{{{key}}}"), value);
}
Some(url)
}
pub fn route_with_params(name: &str, params: &HashMap<String, String>) -> Option<String> {
let registry = ROUTE_REGISTRY.get()?.read().ok()?;
let path_pattern = registry.get(name)?;
let mut url = path_pattern.clone();
for (key, value) in params {
url = url.replace(&format!("{{{key}}}"), value);
}
Some(url)
}
#[derive(Clone, Copy)]
enum Method {
Get,
Post,
Put,
Patch,
Delete,
}
pub type BoxedHandler =
Box<dyn Fn(Request) -> Pin<Box<dyn Future<Output = Response> + Send>> + Send + Sync>;
type RouteValue = (Arc<BoxedHandler>, String);
pub struct Router {
get_routes: MatchitRouter<RouteValue>,
post_routes: MatchitRouter<RouteValue>,
put_routes: MatchitRouter<RouteValue>,
patch_routes: MatchitRouter<RouteValue>,
delete_routes: MatchitRouter<RouteValue>,
route_middleware: HashMap<String, Vec<BoxedMiddleware>>,
fallback_handler: Option<Arc<BoxedHandler>>,
fallback_middleware: Vec<BoxedMiddleware>,
}
impl Router {
pub fn new() -> Self {
Self {
get_routes: MatchitRouter::new(),
post_routes: MatchitRouter::new(),
put_routes: MatchitRouter::new(),
patch_routes: MatchitRouter::new(),
delete_routes: MatchitRouter::new(),
route_middleware: HashMap::new(),
fallback_handler: None,
fallback_middleware: Vec::new(),
}
}
pub fn get_route_middleware(&self, path: &str) -> Vec<BoxedMiddleware> {
self.route_middleware.get(path).cloned().unwrap_or_default()
}
pub(crate) fn add_middleware(&mut self, path: &str, middleware: BoxedMiddleware) {
self.route_middleware
.entry(path.to_string())
.or_default()
.push(middleware);
}
pub(crate) fn set_fallback(&mut self, handler: Arc<BoxedHandler>) {
self.fallback_handler = Some(handler);
}
pub(crate) fn add_fallback_middleware(&mut self, middleware: BoxedMiddleware) {
self.fallback_middleware.push(middleware);
}
pub fn get_fallback(&self) -> Option<(Arc<BoxedHandler>, Vec<BoxedMiddleware>)> {
self.fallback_handler
.as_ref()
.map(|h| (h.clone(), self.fallback_middleware.clone()))
}
pub(crate) fn insert_get(&mut self, path: &str, handler: Arc<BoxedHandler>) {
self.get_routes
.insert(path, (handler, path.to_string()))
.ok();
register_route("GET", path);
}
pub(crate) fn insert_post(&mut self, path: &str, handler: Arc<BoxedHandler>) {
self.post_routes
.insert(path, (handler, path.to_string()))
.ok();
register_route("POST", path);
}
pub(crate) fn insert_put(&mut self, path: &str, handler: Arc<BoxedHandler>) {
self.put_routes
.insert(path, (handler, path.to_string()))
.ok();
register_route("PUT", path);
}
pub(crate) fn insert_patch(&mut self, path: &str, handler: Arc<BoxedHandler>) {
self.patch_routes
.insert(path, (handler, path.to_string()))
.ok();
register_route("PATCH", path);
}
pub(crate) fn insert_delete(&mut self, path: &str, handler: Arc<BoxedHandler>) {
self.delete_routes
.insert(path, (handler, path.to_string()))
.ok();
register_route("DELETE", path);
}
pub fn get<H, Fut>(mut self, path: &str, handler: H) -> RouteBuilder
where
H: Fn(Request) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Response> + Send + 'static,
{
let handler: BoxedHandler = Box::new(move |req| Box::pin(handler(req)));
self.get_routes
.insert(path, (Arc::new(handler), path.to_string()))
.ok();
register_route("GET", path);
RouteBuilder {
router: self,
last_path: path.to_string(),
_last_method: Method::Get,
}
}
pub fn post<H, Fut>(mut self, path: &str, handler: H) -> RouteBuilder
where
H: Fn(Request) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Response> + Send + 'static,
{
let handler: BoxedHandler = Box::new(move |req| Box::pin(handler(req)));
self.post_routes
.insert(path, (Arc::new(handler), path.to_string()))
.ok();
register_route("POST", path);
RouteBuilder {
router: self,
last_path: path.to_string(),
_last_method: Method::Post,
}
}
pub fn put<H, Fut>(mut self, path: &str, handler: H) -> RouteBuilder
where
H: Fn(Request) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Response> + Send + 'static,
{
let handler: BoxedHandler = Box::new(move |req| Box::pin(handler(req)));
self.put_routes
.insert(path, (Arc::new(handler), path.to_string()))
.ok();
register_route("PUT", path);
RouteBuilder {
router: self,
last_path: path.to_string(),
_last_method: Method::Put,
}
}
pub fn patch<H, Fut>(mut self, path: &str, handler: H) -> RouteBuilder
where
H: Fn(Request) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Response> + Send + 'static,
{
let handler: BoxedHandler = Box::new(move |req| Box::pin(handler(req)));
self.patch_routes
.insert(path, (Arc::new(handler), path.to_string()))
.ok();
register_route("PATCH", path);
RouteBuilder {
router: self,
last_path: path.to_string(),
_last_method: Method::Patch,
}
}
pub fn delete<H, Fut>(mut self, path: &str, handler: H) -> RouteBuilder
where
H: Fn(Request) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Response> + Send + 'static,
{
let handler: BoxedHandler = Box::new(move |req| Box::pin(handler(req)));
self.delete_routes
.insert(path, (Arc::new(handler), path.to_string()))
.ok();
register_route("DELETE", path);
RouteBuilder {
router: self,
last_path: path.to_string(),
_last_method: Method::Delete,
}
}
pub fn match_route(
&self,
method: &hyper::Method,
path: &str,
) -> Option<(Arc<BoxedHandler>, HashMap<String, String>, String)> {
let router = match *method {
hyper::Method::GET => &self.get_routes,
hyper::Method::POST => &self.post_routes,
hyper::Method::PUT => &self.put_routes,
hyper::Method::PATCH => &self.patch_routes,
hyper::Method::DELETE => &self.delete_routes,
_ => return None,
};
router.at(path).ok().map(|matched| {
let params: HashMap<String, String> = matched
.params
.iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect();
let (handler, pattern) = matched.value.clone();
(handler, params, pattern)
})
}
}
impl Default for Router {
fn default() -> Self {
Self::new()
}
}
pub struct RouteBuilder {
pub(crate) router: Router,
last_path: String,
#[allow(dead_code)]
_last_method: Method,
}
impl RouteBuilder {
pub fn name(self, name: &str) -> Router {
register_route_name(name, &self.last_path);
self.router
}
pub fn middleware<M: Middleware + 'static>(mut self, middleware: M) -> RouteBuilder {
let type_name = std::any::type_name::<M>();
let short_name = type_name.rsplit("::").next().unwrap_or(type_name);
update_route_middleware(&self.last_path, short_name);
self.router
.add_middleware(&self.last_path, into_boxed(middleware));
self
}
pub fn middleware_boxed(mut self, middleware: BoxedMiddleware) -> RouteBuilder {
update_route_middleware(&self.last_path, "BoxedMiddleware");
self.router
.route_middleware
.entry(self.last_path.clone())
.or_default()
.push(middleware);
self
}
pub fn get<H, Fut>(self, path: &str, handler: H) -> RouteBuilder
where
H: Fn(Request) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Response> + Send + 'static,
{
self.router.get(path, handler)
}
pub fn post<H, Fut>(self, path: &str, handler: H) -> RouteBuilder
where
H: Fn(Request) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Response> + Send + 'static,
{
self.router.post(path, handler)
}
pub fn put<H, Fut>(self, path: &str, handler: H) -> RouteBuilder
where
H: Fn(Request) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Response> + Send + 'static,
{
self.router.put(path, handler)
}
pub fn patch<H, Fut>(self, path: &str, handler: H) -> RouteBuilder
where
H: Fn(Request) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Response> + Send + 'static,
{
self.router.patch(path, handler)
}
pub fn delete<H, Fut>(self, path: &str, handler: H) -> RouteBuilder
where
H: Fn(Request) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Response> + Send + 'static,
{
self.router.delete(path, handler)
}
}
impl From<RouteBuilder> for Router {
fn from(builder: RouteBuilder) -> Self {
builder.router
}
}