use std::path::Path; use std::sync::Arc; use desi_explorer_api::{config, routes, store}; use tracing_subscriber::EnvFilter; #[tokio::main] async fn main() -> anyhow::Result<()> { tracing_subscriber::fmt() .with_env_filter( EnvFilter::try_from_default_env() .unwrap_or_else(|_| EnvFilter::new("desi_explorer_api=info")), ) .init(); load_env_file()?; let config = config::Config::from_env()?; let catalog_store = match &config.desi_data { Some(path) => { tracing::info!(path = %path.display(), "loading DESI data"); store::CatalogStore::load(Path::new(path))? } None => { tracing::warn!("API_DESI_DATA not set, serving placeholder catalogs"); store::CatalogStore::placeholder() } }; let app = routes::app_with_state(Arc::new(catalog_store)); let listener = tokio::net::TcpListener::bind(&config.bind_addr).await?; tracing::info!("DESI Explorer API listening on {}", config.bind_addr); axum::serve(listener, app) .with_graceful_shutdown(shutdown_signal()) .await?; Ok(()) } /// Loads the env file named by `API_ENV_FILE` (if set) into the process /// environment. Existing env vars are not overridden, so values passed /// directly on the command line or by the Makefile take precedence. fn load_env_file() -> anyhow::Result<()> { let path = std::env::var("API_ENV_FILE").unwrap_or_default(); if path.is_empty() { return Ok(()); } match dotenvy::from_path(&path) { Ok(_) => tracing::info!(%path, "loaded env file"), Err(err) => { return Err(anyhow::anyhow!( "failed to load API_ENV_FILE {path:?}: {err}" )) } } Ok(()) } async fn shutdown_signal() { let _ = tokio::signal::ctrl_c().await; tracing::info!("shutting down"); }