Skip to main content

rs_zero/rest/
server.rs

1use std::{future::Future, net::SocketAddr};
2
3use axum::Router;
4
5use crate::rest::{RestConfig, middleware::apply_default_layers};
6
7/// REST server wrapper that keeps axum's router available for extension.
8#[derive(Debug, Clone)]
9pub struct RestServer {
10    config: RestConfig,
11    router: Router,
12}
13
14impl RestServer {
15    /// Creates a server from a config and router.
16    pub fn new(config: RestConfig, router: Router) -> Self {
17        Self { config, router }
18    }
19
20    /// Returns the raw router without applying default middleware.
21    pub fn raw_router(&self) -> Router {
22        self.router.clone()
23    }
24
25    /// Builds the router with rs-zero default middleware.
26    pub fn into_router(self) -> Router {
27        apply_default_layers(self.router, self.config)
28    }
29
30    /// Starts the server with graceful shutdown.
31    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}