1use std::{future::Future, net::SocketAddr};
2
3use axum::Router;
4
5use crate::rest::{RestConfig, middleware::apply_default_layers};
6
7#[derive(Debug, Clone)]
9pub struct RestServer {
10 config: RestConfig,
11 router: Router,
12}
13
14impl RestServer {
15 pub fn new(config: RestConfig, router: Router) -> Self {
17 Self { config, router }
18 }
19
20 pub fn raw_router(&self) -> Router {
22 self.router.clone()
23 }
24
25 pub fn into_router(self) -> Router {
27 apply_default_layers(self.router, self.config)
28 }
29
30 pub async fn serve_with_shutdown<F>(self, addr: SocketAddr, shutdown: F) -> std::io::Result<()>
32 where
33 F: Future<Output = ()> + Send + 'static,
34 {
35 let listener = tokio::net::TcpListener::bind(addr).await?;
36 axum::serve(listener, self.into_router())
37 .with_graceful_shutdown(shutdown)
38 .await
39 }
40}
41
42#[cfg(test)]
43mod tests {
44 use super::RestServer;
45 use crate::rest::{ApiResponse, RestConfig};
46 use axum::{Router, routing::get};
47 use tower::ServiceExt;
48
49 #[tokio::test]
50 async fn server_builds_router() {
51 let router = Router::new().route("/ready", get(|| async { ApiResponse::success("ok") }));
52
53 let service = RestServer::new(RestConfig::default(), router).into_router();
54 let response = service
55 .oneshot(
56 axum::http::Request::builder()
57 .uri("/ready")
58 .body(axum::body::Body::empty())
59 .expect("request"),
60 )
61 .await
62 .expect("response");
63
64 assert_eq!(response.status(), axum::http::StatusCode::OK);
65 }
66}