use clap::Parser; use serde::Deserialize; use std::path::PathBuf; #[derive(Deserialize, Debug)] struct FileConfig { host: Option, port: Option, log_level: Option, jwt_secret: Option, web: Option, } #[derive(Parser, Debug)] #[command(version, about)] struct Cli { #[arg(short, long)] config: Option, #[arg(short, long)] addr: Option, #[arg(short, long)] port: Option, #[arg(short, long)] log_level: Option, #[arg(short, long)] jwt_secret: Option, #[arg(short, long)] web: Option, } #[derive(Debug, Clone)] pub struct Config { pub host: String, pub port: u16, pub log_level: String, pub jwt_secret: String, pub web: String, } impl Config { pub async fn new() -> anyhow::Result { let cli: Cli = Cli::parse(); let file_config: FileConfig = if let Some(config_path) = &cli.config { println!("Loading config from file"); Self::load_file_config(config_path).await? } else { FileConfig { host: None, port: None, log_level: None, jwt_secret: None, web: None, } }; let host: String = cli .addr .or(file_config.host) .unwrap_or_else(|| "0.0.0.0".to_string()); let port: u16 = cli.port.or(file_config.port).unwrap_or(8888); let log_level: String = cli .log_level .or(file_config.log_level) .unwrap_or_else(|| "info".to_string()); let jwt_secret: String = cli .jwt_secret .or(file_config.jwt_secret) .unwrap_or_else(|| "secret".to_string()); let web: String = cli .web .or(file_config.web) .unwrap_or_else(|| "./web".to_string()); Ok(Config { host, port, log_level, jwt_secret, web, }) } async fn load_file_config(path: &PathBuf) -> anyhow::Result { let content = tokio::fs::read_to_string(path).await?; let config: FileConfig = serde_json::from_str(&content)?; Ok(config) } }