use std::net::SocketAddr; use serde::{Deserialize, Serialize}; use unnamed_server::Error; #[derive(Debug, Clone)] pub struct Config { pub listen_addr: SocketAddr, pub jwt_secret: String, pub database_url: String, } impl Config { pub fn builder() -> ConfigBuilder { ConfigBuilder::default() } } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ConfigBuilder { listen_addr: Option, jwt_secret: Option, database_url: Option, } impl ConfigBuilder { pub fn file(self) -> Result { let file = std::env::args() .nth(1) .unwrap_or("/etc/unnamed_server.toml".to_string()); match std::fs::read_to_string(file) { Ok(s) => Ok(self.merge(toml::from_str(&s)?)), Err(err) => { tracing::warn!("Error reading config file: {err}"); Ok(self) } } } pub fn env(self) -> Self { let _ = dotenvy::dotenv(); self.merge(Self { listen_addr: std::env::var("LISTEN_ADDR").ok(), jwt_secret: std::env::var("JWT_SECRET").ok(), database_url: std::env::var("DATABASE_URL").ok(), }) } /// Merge self with other, overwriting any existing values on self with other's. fn merge(self, other: Self) -> Self { Self { listen_addr: other.listen_addr.or(self.listen_addr), jwt_secret: other.jwt_secret.or(self.jwt_secret), database_url: other.database_url.or(self.database_url), } } pub fn build(self) -> Result { Ok(Config { listen_addr: self .listen_addr .and_then(|s| s.parse().ok()) .ok_or_else(|| Error::Config("listen_addr".to_string()))?, jwt_secret: self .jwt_secret .ok_or_else(|| Error::Config("jwt_secret".to_string()))?, database_url: self .database_url .ok_or_else(|| Error::Config("database_url".to_string()))?, }) } } impl Default for ConfigBuilder { fn default() -> Self { Self { listen_addr: Some("127.0.0.1:30000".to_string()), jwt_secret: None, database_url: None, } } }