use std::fmt::Display; use std::net::SocketAddr; use std::path::PathBuf; use crate::{Error, Result}; use figment::providers::{Env, Format, Serialized, Toml}; use serde::{Deserialize, Serialize}; use figment::Figment; #[derive(Debug, Deserialize, Serialize)] pub struct DBConfig { user: String, password: Option, host: String, name: String, string: Option, } impl Display for DBConfig { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "{}", self.generate_db_string(true).map_err(|_| std::fmt::Error)? ) } } impl Default for DBConfig { fn default() -> Self { DBConfig { user: "postgres".to_owned(), password: None, host: "localhost".to_owned(), name: "compendium".to_owned(), string: None, } } } impl DBConfig { pub fn generate_db_string(&self, ofuscate: bool) -> Result { if let Some(value) = &self.string { return Ok(value.clone()); } let db_password = if ofuscate { let mut db_password = self .password .clone() .ok_or(Error::Runtime("No database password provided"))?; let password_length = db_password.len(); let visible_characters = 4; if password_length <= visible_characters { // Hide all the password let filler = "*".repeat(password_length); db_password.replace_range(0..password_length, &filler.to_owned()); } else { // Hide only a portion of the password let filler = "*".repeat(password_length - visible_characters); db_password.replace_range(visible_characters..password_length, &filler.to_owned()); }; db_password } else { self.password .clone() .ok_or(Error::Runtime("No database password provided"))? }; let db_string = format!( "postgres://{}:{}@{}/{}", self.user, db_password, self.host, self.name, ); Ok(db_string.to_owned()) } } #[derive(Debug, Deserialize, Serialize)] pub struct Config { pub db: DBConfig, pub addr: SocketAddr, } impl Default for Config { fn default() -> Self { Config { db: DBConfig::default(), addr: "0.0.0.0:3000".parse().unwrap(), } } } impl Config { pub fn new(path: PathBuf) -> Result { let config: Config = Figment::from(Serialized::defaults(Config::default())) .merge(Toml::file(path)) .merge(Env::prefixed("CPD_").split("_")) .extract()?; Ok(config) } }