use secrecy::{ExposeSecret, SecretString}; use serde::Deserialize; pub fn get_configuration() -> Result { let base_path = std::env::current_dir().expect("Failed to determine the current directory"); let config_dir = base_path.join("configuration"); let environment: Environment = std::env::var("APP_ENVIRONMENT") .unwrap_or_else(|_| "local".into()) .try_into() .expect("Failed to parse APP_ENVIRONMENT"); let environment_filename = format!("{}.yaml", environment.as_str()); let settings = config::Config::builder() .add_source(config::File::from(config_dir.join("base.yaml"))) .add_source(config::File::from(config_dir.join(environment_filename))) .build()?; settings.try_deserialize::() } pub enum Environment { Local, Production, } impl Environment { pub fn as_str(&self) -> &str { match self { Environment::Local => "local", Environment::Production => "production", } } } impl TryFrom for Environment { type Error = String; fn try_from(value: String) -> Result { match value.to_lowercase().as_str() { "local" => Ok(Environment::Local), "production" => Ok(Environment::Production), other => Err(format!( "{} is not a supported environment. Use either `local` or `production`.", other )), } } } #[derive(Deserialize)] pub struct Settings { pub application: ApplicationSettings, pub database: DatabaseSettings, } #[derive(Deserialize)] pub struct ApplicationSettings { pub port: u16, pub host: String, } #[derive(Deserialize)] pub struct DatabaseSettings { pub username: String, pub password: SecretString, pub port: u16, pub host: String, pub database_name: String, } impl DatabaseSettings { pub fn connection_string(&self) -> SecretString { format!( "postgres://{}:{}@{}:{}/{}", self.username, self.password.expose_secret(), self.host, self.port, self.database_name ) .into() } pub fn connection_string_without_db(&self) -> SecretString { format!( "postgres://{}:{}@{}:{}", self.username, self.password.expose_secret(), self.host, self.port ) .into() } }