diff --git a/src/axum/app.rs b/src/axum/app.rs index 64c4ba5..30e8633 100644 --- a/src/axum/app.rs +++ b/src/axum/app.rs @@ -44,6 +44,13 @@ impl AppBuilder { Self::default() } + pub fn from_router(router: Router) -> Self { + Self { + router, + ..Self::default() + } + } + pub fn route(mut self, route: Router) -> Self { self.router = self.router.merge(route); self @@ -106,6 +113,23 @@ impl AppBuilder { self } + /// Creates the app with the given options. + /// This method is useful for testing purposes. + /// Options used for configuring the listener will be lost. + pub fn build(self) -> Router { + let mut app = self.router; + if let Some(cors) = self.cors { + app = app.layer(cors); + } + app.layer( + self.tracing.unwrap_or( + TraceLayer::new_for_http() + .make_span_with(trace::DefaultMakeSpan::new().level(Level::INFO)) + .on_response(trace::DefaultOnResponse::new().level(Level::INFO)), + ), + ) + } + /// Build the app and start the server /// # Default Options /// - IP == 0.0.0.0 @@ -118,10 +142,10 @@ impl AppBuilder { let listener = self.listener().await?; if self.normalize_path.unwrap_or(true) { - let app = NormalizePathLayer::trim_trailing_slash().layer(self.create_app()); + let app = NormalizePathLayer::trim_trailing_slash().layer(self.build()); axum::serve(listener, ServiceExt::::into_make_service(app)).await?; } else { - let app = self.create_app(); + let app = self.build(); axum::serve(listener, app.into_make_service()).await?; }; Ok(()) @@ -132,20 +156,6 @@ impl AppBuilder { info!("Initializing server on: {addr}"); TcpListener::bind(&addr).await } - - fn create_app(self) -> Router { - let mut app = self.router; - if let Some(cors) = self.cors { - app = app.layer(cors); - } - app.layer( - self.tracing.unwrap_or( - TraceLayer::new_for_http() - .make_span_with(trace::DefaultMakeSpan::new().level(Level::INFO)) - .on_response(trace::DefaultOnResponse::new().level(Level::INFO)), - ), - ) - } } fn fmt_trace() -> Result<(), String> { diff --git a/src/axum/router.rs b/src/axum/router.rs index 0f3e108..d45cfb0 100644 --- a/src/axum/router.rs +++ b/src/axum/router.rs @@ -29,12 +29,28 @@ macro_rules! router { $body } }; + ($body:expr; $state:ident: $($bound:tt),*) => { + pub fn router<$state: $($bound+)* 'static>() -> axum::Router<$state> { + $body + } + }; + ($body:expr; $generic:ident: $($bound:tt),* -> $state:ty) => { + pub fn router<$generic: $($bound+)* 'static>() -> axum::Router<$state<$generic>> { + $body + } + }; ($route:expr, $router:expr) => { router!(axum::Router::new().nest($route, $router)); }; ($route:expr, $router:expr, $state:ty) => { router!(axum::Router::new().nest($route, $router); $state); }; + ($route:expr, $router:expr, $state:ident: $($bound:tt),*) => { + router!(axum::Router::new().nest($route, $router); $state: $($bound),*); + }; + ($route:expr, $router:expr, $generic:ident: $($bound:tt),* -> $state:ty) => { + router!(axum::Router::new().nest($route, $router); $generic: $($bound),* -> $state); + }; ($($method:ident $route:expr => $func:expr),* $(,)?) => { router!($crate::routes!($($method $route => $func),*)); }; @@ -112,6 +128,18 @@ mod tests { ); } + #[test] + fn test_nested_router_with_generic_state() { + router!( + "/simplify", + routes!( + get "/:exp" => || async {}, + get "/table/:exp" => |_state: State| async {} + ), + T: Clone, Send, Sync + ); + } + #[test] fn test_routes() { let _router: Router = routes!(