123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106 |
- use serde_aux::field_attributes::deserialize_number_from_string;
- use sqlx::postgres::{PgConnectOptions, PgSslMode};
- use std::convert::{TryFrom, TryInto};
- #[derive(serde::Deserialize, Clone, Debug)]
- pub struct Settings {
- pub database: DatabaseSettings,
- pub application: ApplicationSettings,
- }
- // We are using 127.0.0.1 as our host in address, we are instructing our
- // application to only accept connections coming from the same machine. However,
- // request from the hose machine which is not seen as local by our Docker image.
- //
- // Using 0.0.0.0 as host to instruct our application to accept connections from
- // any network interface. So using 127.0.0.1 for our local development and set
- // it to 0.0.0.0 in our Docker images.
- //
- #[derive(serde::Deserialize, Clone, Debug)]
- pub struct ApplicationSettings {
- #[serde(deserialize_with = "deserialize_number_from_string")]
- pub port: u16,
- pub host: String,
- }
- #[derive(serde::Deserialize, Clone, Debug)]
- pub struct DatabaseSettings {
- pub username: String,
- pub password: String,
- #[serde(deserialize_with = "deserialize_number_from_string")]
- pub port: u16,
- pub host: String,
- pub database_name: String,
- pub require_ssl: bool,
- }
- impl DatabaseSettings {
- pub fn without_db(&self) -> PgConnectOptions {
- let ssl_mode = if self.require_ssl {
- PgSslMode::Require
- } else {
- PgSslMode::Prefer
- };
- PgConnectOptions::new()
- .host(&self.host)
- .username(&self.username)
- .password(&self.password)
- .port(self.port)
- .ssl_mode(ssl_mode)
- }
- pub fn with_db(&self) -> PgConnectOptions {
- self.without_db().database(&self.database_name)
- }
- }
- pub fn get_configuration() -> Result<Settings, config::ConfigError> {
- let mut settings = config::Config::default();
- let base_path = std::env::current_dir().expect("Failed to determine the current directory");
- let configuration_dir = base_path.join("configuration");
- settings.merge(config::File::from(configuration_dir.join("base")).required(true))?;
- let environment: Environment = std::env::var("APP_ENVIRONMENT")
- .unwrap_or_else(|_| "local".into())
- .try_into()
- .expect("Failed to parse APP_ENVIRONMENT.");
- settings.merge(config::File::from(configuration_dir.join(environment.as_str())).required(true))?;
- // Add in settings from environment variables (with a prefix of APP and '__' as
- // separator) E.g. `APP_APPLICATION__PORT=5001 would set
- // `Settings.application.port`
- settings.merge(config::Environment::with_prefix("app").separator("__"))?;
- settings.try_into()
- }
- /// The possible runtime environment for our application.
- pub enum Environment {
- Local,
- Production,
- }
- impl Environment {
- pub fn as_str(&self) -> &'static str {
- match self {
- Environment::Local => "local",
- Environment::Production => "production",
- }
- }
- }
- impl TryFrom<String> for Environment {
- type Error = String;
- fn try_from(s: String) -> Result<Self, Self::Error> {
- match s.to_lowercase().as_str() {
- "local" => Ok(Self::Local),
- "production" => Ok(Self::Production),
- other => Err(format!(
- "{} is not a supported environment. Use either `local` or `production`.",
- other
- )),
- }
- }
- }
|