this repo has no description
at main 122 lines 3.6 kB view raw
1use secrecy::{ExposeSecret, SecretString}; 2use serde::Deserialize; 3use serde_aux::field_attributes::deserialize_number_from_string; 4use sqlx::postgres::{PgConnectOptions, PgSslMode}; 5use std::convert::{TryFrom, TryInto}; 6 7#[derive(Deserialize, Clone, Debug)] 8pub struct Settings { 9 pub application: ApplicationSettings, 10 pub database: DatabaseSettings, 11 pub hosts: HostnameSettings, 12 pub redis_uri: SecretString, 13} 14 15#[derive(Deserialize, Clone, Debug)] 16pub struct ApplicationSettings { 17 pub base_url: String, 18 pub hmac_secret: SecretString, 19 pub host: String, 20 #[serde(deserialize_with = "deserialize_number_from_string")] 21 pub port: u16, 22 pub session_key: String, 23} 24 25#[derive(Deserialize, Clone, Debug)] 26pub struct DatabaseSettings { 27 pub database_name: String, 28 pub host: String, 29 pub password: SecretString, 30 #[serde(deserialize_with = "deserialize_number_from_string")] 31 pub port: u16, 32 pub require_ssl: bool, 33 pub username: String, 34} 35 36impl DatabaseSettings { 37 pub fn connect_options(&self) -> PgConnectOptions { 38 let ssl_mode = if self.require_ssl { 39 PgSslMode::Require 40 } else { 41 PgSslMode::Prefer 42 }; 43 PgConnectOptions::new() 44 .host(&self.host) 45 .username(&self.username) 46 .password(self.password.expose_secret()) 47 .port(self.port) 48 .ssl_mode(ssl_mode) 49 .database(&self.database_name) 50 } 51} 52 53#[derive(Deserialize, Clone, Debug)] 54pub struct HostnameSettings { 55 pub client: String, 56} 57 58pub fn get_configuration() -> Result<Settings, config::ConfigError> { 59 let base_path = std::env::current_dir().expect("Failed to determine the current directory"); 60 let configuration_directory = base_path.join("configuration"); 61 let environment: Environment = std::env::var("APP_ENVIRONMENT") 62 .unwrap_or_else(|_| "local".into()) 63 .try_into() 64 .expect("Failed to parse APP_ENVIRONMENT."); 65 let _ = dotenvy::from_filename(environment.dotenv()).unwrap_or_default(); 66 let environment_filename = format!("{}.yaml", environment.as_str()); 67 let settings = config::Config::builder() 68 .add_source(config::File::from( 69 configuration_directory.join("base.yaml"), 70 )) 71 .add_source(config::File::from( 72 configuration_directory.join(environment_filename), 73 )) 74 .add_source( 75 config::Environment::with_prefix("APP") 76 .prefix_separator("_") 77 .separator("__"), 78 ) 79 .build()?; 80 81 settings.try_deserialize::<Settings>() 82} 83 84pub enum Environment { 85 Local, 86 Production, 87 Test, 88} 89 90impl Environment { 91 pub fn as_str(&self) -> &'static str { 92 match self { 93 Environment::Local => "local", 94 Environment::Production => "production", 95 Environment::Test => "test", 96 } 97 } 98 99 pub fn dotenv(&self) -> &'static str { 100 match self { 101 Environment::Local => ".env", 102 Environment::Production => ".env", 103 Environment::Test => ".env.test", 104 } 105 } 106} 107 108impl TryFrom<String> for Environment { 109 type Error = String; 110 111 fn try_from(s: String) -> Result<Self, Self::Error> { 112 match s.to_lowercase().as_str() { 113 "local" => Ok(Self::Local), 114 "production" => Ok(Self::Production), 115 "test" => Ok(Self::Test), 116 other => Err(format!( 117 "{} is not a supported environment. Use either `local`, `test` or `production`.", 118 other 119 )), 120 } 121 } 122}