use std::convert::Infallible;
use axum::extract::Request;
use axum::response::IntoResponse;
use axum::routing::Route;
use axum::routing::Router;
use tower::Layer;
use tower::Service;
pub use self::metadata::RouteMetadata;
pub use self::metadata::RouteMetadataSet;
use crate::handler::GalvynHandler;
use crate::handler::HandlerMeta;
use crate::middleware::GalvynMiddleware;
mod metadata;
#[derive(Debug, Default)]
pub struct GalvynRouter {
handlers: Vec<GalvynRoute>,
router: Router,
extensions: RouteMetadataSet,
}
impl GalvynRouter {
pub fn new() -> Self {
Self::default()
}
pub fn with_extension(extension: impl RouteMetadata) -> Self {
Self::new().metadata(extension)
}
pub fn handler(mut self, handler: impl GalvynHandler) -> Self {
self.push_handler(GalvynRoute::new(handler.meta()));
self.router = self
.router
.route(handler.meta().path, handler.method_router());
self
}
pub fn metadata(mut self, extension: impl RouteMetadata) -> Self {
for handler in &mut self.handlers {
handler.extensions.insert(extension.clone());
}
self.extensions.insert(extension);
self
}
fn push_handler(&mut self, mut handler: GalvynRoute) {
handler.extensions.merge(&self.extensions);
self.handlers.push(handler);
}
pub fn finish(self) -> (Router, Vec<GalvynRoute>) {
(self.router, self.handlers)
}
#[track_caller]
pub fn nest(mut self, path: &str, other: GalvynRouter) -> Self {
if path.is_empty() || path == "/" {
panic!("Nesting at the root is no longer supported. Use merge instead.");
}
if !path.starts_with('/') {
panic!("Paths must start with a slash.");
}
for mut handler in other.handlers {
handler.path = if path.ends_with('/') {
format!("{path}{}", handler.path.trim_start_matches('/'))
} else if handler.path == "/" {
path.into()
} else {
format!("{path}{}", handler.path)
};
self.push_handler(handler);
}
self.router = self.router.nest(path, other.router);
self
}
pub fn merge(mut self, other: GalvynRouter) -> Self {
for handler in other.handlers {
self.push_handler(handler);
}
self.router = self.router.merge(other.router);
self
}
pub fn wrap(mut self, middleware: impl GalvynMiddleware) -> Self {
self.router = self.router.layer(middleware.into_layer());
self
}
pub fn layer<L>(mut self, layer: L) -> Self
where
L: Layer<Route> + Clone + Send + Sync + 'static,
L::Service: Service<Request> + Clone + Send + Sync + 'static,
<L::Service as Service<Request>>::Response: IntoResponse + 'static,
<L::Service as Service<Request>>::Error: Into<Infallible> + 'static,
<L::Service as Service<Request>>::Future: Send + 'static,
{
self.router = self.router.layer(layer);
self
}
pub fn route_layer<L>(mut self, layer: L) -> Self
where
L: Layer<Route> + Clone + Send + Sync + 'static,
L::Service: Service<Request> + Clone + Send + Sync + 'static,
<L::Service as Service<Request>>::Response: IntoResponse + 'static,
<L::Service as Service<Request>>::Error: Into<Infallible> + 'static,
<L::Service as Service<Request>>::Future: Send + 'static,
{
self.router = self.router.route_layer(layer);
self
}
}
#[derive(Debug)]
pub struct GalvynRoute {
pub handler: HandlerMeta,
pub path: String,
pub extensions: RouteMetadataSet,
}
impl GalvynRoute {
pub fn new(original: HandlerMeta) -> Self {
Self {
path: original.path.to_string(),
handler: original,
extensions: RouteMetadataSet::default(),
}
}
}