use crate::routers::ServerRouter;
use reinhardt_middleware::Middleware;
pub type RouteInfo = Vec<(String, Option<String>, Option<String>, Vec<hyper::Method>)>;
pub struct RouteGroup {
router: ServerRouter,
}
impl RouteGroup {
pub fn new() -> Self {
Self {
router: ServerRouter::new(),
}
}
pub fn with_prefix(mut self, prefix: impl Into<String>) -> Self {
self.router = self.router.with_prefix(prefix);
self
}
pub fn with_namespace(mut self, namespace: impl Into<String>) -> Self {
self.router = self.router.with_namespace(namespace);
self
}
pub fn with_middleware<M: Middleware + 'static>(mut self, middleware: M) -> Self {
self.router = self.router.with_middleware(middleware);
self
}
pub fn function<F, Fut>(mut self, path: &str, method: hyper::Method, func: F) -> Self
where
F: Fn(reinhardt_http::Request) -> Fut + Send + Sync + 'static,
Fut: std::future::Future<
Output = reinhardt_core::exception::Result<reinhardt_http::Response>,
> + Send
+ 'static,
{
self.router = self.router.function(path, method, func);
self
}
pub fn handler_with_method<H: reinhardt_http::Handler + 'static>(
mut self,
path: &str,
method: hyper::Method,
handler: H,
) -> Self {
self.router = self.router.handler_with_method(path, method, handler);
self
}
#[deprecated(
since = "0.2.0",
note = "Use `#[get(\"/path\", name = \"name\")]` + `.endpoint()` instead"
)]
pub fn function_named<F, Fut>(
mut self,
path: &str,
method: hyper::Method,
name: &str,
func: F,
) -> Self
where
F: Fn(reinhardt_http::Request) -> Fut + Send + Sync + 'static,
Fut: std::future::Future<
Output = reinhardt_core::exception::Result<reinhardt_http::Response>,
> + Send
+ 'static,
{
#[allow(deprecated)]
{
self.router = self.router.function_named(path, method, name, func);
}
self
}
#[deprecated(
since = "0.2.0",
note = "Use `#[get(\"/path\", name = \"name\")]` + `.endpoint()` instead"
)]
pub fn handler_with_method_named<H: reinhardt_http::Handler + 'static>(
mut self,
path: &str,
method: hyper::Method,
name: &str,
handler: H,
) -> Self {
#[allow(deprecated)]
{
self.router = self
.router
.handler_with_method_named(path, method, name, handler);
}
self
}
#[cfg(feature = "viewsets")]
pub fn viewset<V: reinhardt_views::viewsets::ViewSet + 'static>(
mut self,
prefix: &str,
viewset: V,
) -> Self {
self.router = self.router.viewset(prefix, viewset);
self
}
#[cfg(feature = "viewsets")]
pub fn viewset_with_actions<V, M>(
self,
prefix: &str,
viewset: V,
_marker: std::marker::PhantomData<M>,
) -> Self
where
V: reinhardt_views::viewsets::ViewSet + 'static,
M: 'static,
{
reinhardt_views::viewsets::bridge_marker_actions_to_viewset(
std::any::type_name::<M>(),
std::any::type_name::<V>(),
);
self.viewset(prefix, viewset)
}
pub fn view<V>(mut self, path: &str, view: V) -> Self
where
V: reinhardt_http::Handler + 'static,
{
self.router = self.router.view(path, view);
self
}
#[deprecated(
since = "0.2.0",
note = "Use `#[get(\"/path\", name = \"name\")]` + `.endpoint()` instead"
)]
pub fn view_named<V>(mut self, path: &str, name: &str, view: V) -> Self
where
V: reinhardt_http::Handler + 'static,
{
#[allow(deprecated)]
{
self.router = self.router.view_named(path, name, view);
}
self
}
pub fn nest(mut self, child: RouteGroup) -> Self {
let child_prefix = child.router.prefix().to_string();
self.router = self.router.mount(&child_prefix, child.router);
self
}
pub fn prefix(&self) -> &str {
self.router.prefix()
}
pub fn namespace(&self) -> Option<&str> {
self.router.namespace()
}
pub fn children_count(&self) -> usize {
self.router.children_count()
}
pub fn get_all_routes(&self) -> RouteInfo {
self.router.get_all_routes()
}
pub fn build(self) -> ServerRouter {
self.router
}
}
impl Default for RouteGroup {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use hyper::Method;
use reinhardt_http::{Request, Response, Result};
use reinhardt_middleware::LoggingMiddleware;
async fn test_handler(_req: Request) -> Result<Response> {
Ok(Response::ok())
}
#[test]
fn test_route_group_new() {
let group = RouteGroup::new();
let router = group.build();
assert_eq!(router.prefix(), "");
}
#[test]
fn test_route_group_with_prefix() {
let group = RouteGroup::new().with_prefix("/api/v1");
let router = group.build();
assert_eq!(router.prefix(), "/api/v1");
}
#[test]
fn test_route_group_with_namespace() {
let group = RouteGroup::new().with_namespace("v1");
let router = group.build();
assert_eq!(router.namespace(), Some("v1"));
}
#[test]
fn test_route_group_with_middleware() {
let group = RouteGroup::new().with_middleware(LoggingMiddleware::new());
let _router = group.build();
}
#[test]
fn test_route_group_function() {
let group = RouteGroup::new().function("/health", Method::GET, test_handler);
let _router = group.build();
}
#[test]
fn test_route_group_nested() {
let auth_group =
RouteGroup::new()
.with_prefix("/auth/")
.function("/login", Method::POST, test_handler);
let group = RouteGroup::new().with_prefix("/api/").nest(auth_group);
let router = group.build();
assert_eq!(router.children_count(), 1);
}
#[test]
fn test_route_group_multiple_middleware() {
let group = RouteGroup::new()
.with_middleware(LoggingMiddleware::new())
.with_middleware(LoggingMiddleware::new())
.function("/test", Method::GET, test_handler);
let _router = group.build();
}
}
#[cfg(all(test, feature = "viewsets"))]
mod viewset_with_actions_tests {
use super::*;
use async_trait::async_trait;
use reinhardt_http::{Request, Response, Result};
use reinhardt_views::viewsets::{Action, ViewSet};
use rstest::rstest;
use std::marker::PhantomData;
#[derive(Debug, Clone)]
struct DummyViewSet {
basename: String,
}
#[async_trait]
impl ViewSet for DummyViewSet {
fn get_basename(&self) -> &str {
&self.basename
}
async fn dispatch(&self, _request: Request, _action: Action) -> Result<Response> {
Ok(Response::ok())
}
}
struct DummyImpl;
#[rstest]
fn viewset_with_actions_is_equivalent_to_viewset() {
let group_a = RouteGroup::new().viewset(
"/users",
DummyViewSet {
basename: "users".to_string(),
},
);
let group_b = RouteGroup::new().viewset_with_actions(
"/users",
DummyViewSet {
basename: "users".to_string(),
},
PhantomData::<DummyImpl>,
);
let mut router_a = group_a.build();
let mut router_b = group_b.build();
let _ = router_a.register_all_routes();
let _ = router_b.register_all_routes();
let routes_a = router_a.get_all_routes();
let routes_b = router_b.get_all_routes();
assert_eq!(routes_a, routes_b);
}
}