Skip to main content

doido_controller/
server.rs

1//! Convenience entry point for booting an application's HTTP server.
2
3use crate::config::{self, ServerConfig};
4use crate::stack::MiddlewareStack;
5
6/// Resolve the listen address, letting explicit `bind`/`port` overrides (e.g.
7/// `doido server --port 4000`) win over `config/<env>.yml`.
8fn resolve_addr(server: &ServerConfig, bind: Option<&str>, port: Option<u16>) -> String {
9    let bind = bind.unwrap_or(&server.bind);
10    let port = port.unwrap_or(server.port);
11    format!("{bind}:{port}")
12}
13
14/// Boots the HTTP server for `router` using `config/<env>.yml` (the environment
15/// comes from [`doido_core::Environment::get_env`]). Missing config falls back
16/// to `0.0.0.0:3000`.
17pub async fn start_server(router: crate::axum::Router) -> std::io::Result<()> {
18    start_server_with(router, None, None).await
19}
20
21/// Like [`start_server`], but with optional `bind`/`port` overrides that take
22/// precedence over the config file (used by `doido server --port`/`--bind`).
23pub async fn start_server_with(
24    router: crate::axum::Router,
25    bind: Option<String>,
26    port: Option<u16>,
27) -> std::io::Result<()> {
28    let config = config::load();
29    let addr = resolve_addr(config.server(), bind.as_deref(), port);
30
31    // Apply the always-on middleware (request/response logging + panic
32    // recovery) so every request is traced through the global subscriber.
33    let router = MiddlewareStack::default().apply(router);
34
35    let listener = tokio::net::TcpListener::bind(&addr).await?;
36    tracing::info!("listening on http://{addr}");
37    tracing::info!("routes:\n{}", crate::route_table::format_routes());
38    crate::axum::serve(listener, router).await
39}
40
41#[cfg(test)]
42mod tests {
43    use super::*;
44
45    #[test]
46    fn resolve_addr_prefers_overrides_then_config() {
47        let server = ServerConfig {
48            bind: "0.0.0.0".into(),
49            port: 3000,
50        };
51        assert_eq!(resolve_addr(&server, None, None), "0.0.0.0:3000");
52        assert_eq!(resolve_addr(&server, None, Some(4000)), "0.0.0.0:4000");
53        assert_eq!(
54            resolve_addr(&server, Some("127.0.0.1"), None),
55            "127.0.0.1:3000"
56        );
57        assert_eq!(
58            resolve_addr(&server, Some("127.0.0.1"), Some(8080)),
59            "127.0.0.1:8080"
60        );
61    }
62}