Skip to main content

cargo_coupling/web/
server.rs

1//! Web server for coupling visualization
2//!
3//! Provides an HTTP server using Axum to serve the visualization UI
4//! and JSON API endpoints.
5
6use std::net::SocketAddr;
7use std::sync::Arc;
8
9use axum::Router;
10use tokio::net::TcpListener;
11
12use crate::balance::IssueThresholds;
13use crate::metrics::ProjectMetrics;
14
15use super::routes;
16
17/// Shared application state
18pub struct AppState {
19    pub metrics: ProjectMetrics,
20    pub thresholds: IssueThresholds,
21    pub api_endpoint: Option<String>,
22}
23
24/// Configuration for the web server
25pub struct ServerConfig {
26    pub port: u16,
27    pub open_browser: bool,
28    pub api_endpoint: Option<String>,
29}
30
31impl Default for ServerConfig {
32    fn default() -> Self {
33        Self {
34            port: 3000,
35            open_browser: true,
36            api_endpoint: None,
37        }
38    }
39}
40
41/// Start the web server and serve the visualization
42pub async fn start_server(
43    metrics: ProjectMetrics,
44    thresholds: IssueThresholds,
45    config: ServerConfig,
46) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
47    let state = Arc::new(AppState {
48        metrics,
49        thresholds,
50        api_endpoint: config.api_endpoint.clone(),
51    });
52
53    let app = Router::new()
54        .merge(routes::api_routes())
55        .merge(routes::static_routes())
56        .with_state(state);
57
58    let addr = SocketAddr::from(([127, 0, 0, 1], config.port));
59    let listener = TcpListener::bind(addr).await?;
60
61    let url = format!("http://localhost:{}", config.port);
62    eprintln!("Starting web server at {}", url);
63
64    if config.open_browser {
65        eprintln!("Opening browser...");
66        if let Err(e) = open::that(&url) {
67            eprintln!("Warning: Could not open browser: {}", e);
68            eprintln!("Please open {} manually", url);
69        }
70    }
71
72    eprintln!("Press Ctrl+C to stop the server");
73
74    axum::serve(listener, app).await?;
75
76    Ok(())
77}