1use std::path::PathBuf;
10use std::sync::Arc;
11
12use cvx_api::router::build_router;
13use cvx_api::state::AppState;
14use cvx_core::CvxConfig;
15use tokio::net::TcpListener;
16use tracing_subscriber::EnvFilter;
17
18#[tokio::main]
19async fn main() -> anyhow::Result<()> {
20 tracing_subscriber::fmt()
22 .with_env_filter(
23 EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")),
24 )
25 .init();
26
27 let config = load_config()?;
29 let host = config.server.host.clone();
30 let port = config.server.port;
31 let addr = format!("{host}:{port}");
32
33 tracing::info!("Configuration loaded");
34 tracing::debug!(?config);
35
36 let state = Arc::new(AppState::new());
38 let app = build_router(state);
39
40 let listener = TcpListener::bind(&addr).await?;
42 tracing::info!(
43 "ChronosVector v{} listening on {addr}",
44 env!("CARGO_PKG_VERSION")
45 );
46
47 axum::serve(listener, app)
48 .with_graceful_shutdown(shutdown_signal())
49 .await?;
50
51 tracing::info!("Server shut down gracefully");
52 Ok(())
53}
54
55fn load_config() -> anyhow::Result<CvxConfig> {
57 let config_path = std::env::var("CVX_CONFIG")
59 .map(PathBuf::from)
60 .ok()
61 .or_else(|| {
62 let default = PathBuf::from("config.toml");
63 default.exists().then_some(default)
64 });
65
66 let mut config = if let Some(path) = config_path {
67 let content = std::fs::read_to_string(&path)?;
68 tracing::info!("Loading config from {}", path.display());
69 CvxConfig::parse(&content)?
70 } else {
71 tracing::info!("Using default configuration");
72 CvxConfig::default()
73 };
74
75 if let Ok(host) = std::env::var("CVX_HOST") {
77 config.server.host = host;
78 }
79 if let Ok(port) = std::env::var("CVX_PORT") {
80 config.server.port = port.parse().unwrap_or(3000);
81 }
82
83 Ok(config)
84}
85
86async fn shutdown_signal() {
87 let ctrl_c = async {
88 tokio::signal::ctrl_c()
89 .await
90 .expect("failed to install Ctrl+C handler");
91 };
92
93 #[cfg(unix)]
94 let terminate = async {
95 tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())
96 .expect("failed to install SIGTERM handler")
97 .recv()
98 .await;
99 };
100
101 #[cfg(not(unix))]
102 let terminate = std::future::pending::<()>();
103
104 tokio::select! {
105 () = ctrl_c => tracing::info!("Received Ctrl+C"),
106 () = terminate => tracing::info!("Received SIGTERM"),
107 }
108}