use std::{collections::HashMap, fs::File, path::PathBuf, sync::Arc}; use tower_http::services::ServeDir; use tracing::level_filters::LevelFilter; use tracing_subscriber::EnvFilter; use statsrv::service::Service; #[cfg(not(debug_assertions))] const DEFAULT_CONFIG: &str = "/etc/statsrv.toml"; #[cfg(debug_assertions)] const DEFAULT_CONFIG: &str = "./config.toml"; #[tokio::main] async fn main() -> Result<(), Box> { let filter = EnvFilter::builder() .with_default_directive(LevelFilter::INFO.into()) .from_env_lossy(); tracing_subscriber::fmt().with_env_filter(filter).init(); let config = match Config::parse() { Ok(c) => c, Err(err) => { tracing::debug!("Failed to read config file: `{err}`"); tracing::debug!("Using default config values"); Default::default() } }; let state = config .services .into_iter() .map(|(name, service)| (name, service.into())) .collect(); let router = statsrv::router() .with_state(Arc::new(state)) .nest_service("/", ServeDir::new(config.root)) .layer(tower_http::trace::TraceLayer::new_for_http()); let listener = tokio::net::TcpListener::bind(config.address).await.unwrap(); tracing::info!("listening on {}", listener.local_addr().unwrap()); axum::serve(listener, router).await.map_err(Into::into) } #[derive(Debug, Clone, serde::Deserialize)] #[serde(default)] pub struct Config { pub root: PathBuf, pub address: String, pub services: HashMap, } impl Config { fn parse() -> Result> { let config_path = std::env::args().nth(1).unwrap_or_else(|| { tracing::debug!("Falling back to default config location"); DEFAULT_CONFIG.to_string() }); let config_file = File::open(&config_path)?; let config_toml = std::io::read_to_string(config_file)?; toml::from_str(&config_toml).map_err(Into::into) } } impl Default for Config { fn default() -> Self { Self { root: PathBuf::from("./"), address: String::from("127.0.0.1:8080"), services: Default::default(), } } }