configuration.rs 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. use serde_aux::field_attributes::deserialize_number_from_string;
  2. use sqlx::postgres::{PgConnectOptions, PgSslMode};
  3. use std::convert::{TryFrom, TryInto};
  4. #[derive(serde::Deserialize, Clone)]
  5. pub struct Settings {
  6. pub database: DatabaseSettings,
  7. pub application: ApplicationSettings,
  8. }
  9. #[derive(serde::Deserialize, Clone)]
  10. pub struct ApplicationSettings {
  11. #[serde(deserialize_with = "deserialize_number_from_string")]
  12. pub port: u16,
  13. pub host: String,
  14. pub base_url: String,
  15. }
  16. #[derive(serde::Deserialize, Clone)]
  17. pub struct DatabaseSettings {
  18. pub username: String,
  19. pub password: String,
  20. #[serde(deserialize_with = "deserialize_number_from_string")]
  21. pub port: u16,
  22. pub host: String,
  23. pub database_name: String,
  24. pub require_ssl: bool,
  25. }
  26. impl DatabaseSettings {
  27. pub fn without_db(&self) -> PgConnectOptions {
  28. let ssl_mode = if self.require_ssl {
  29. PgSslMode::Require
  30. } else {
  31. PgSslMode::Prefer
  32. };
  33. PgConnectOptions::new()
  34. .host(&self.host)
  35. .username(&self.username)
  36. .password(&self.password)
  37. .port(self.port)
  38. .ssl_mode(ssl_mode)
  39. }
  40. pub fn with_db(&self) -> PgConnectOptions { self.without_db().database(&self.database_name) }
  41. }
  42. pub fn get_configuration() -> Result<Settings, config::ConfigError> {
  43. let mut settings = config::Config::default();
  44. let base_path = std::env::current_dir().expect("Failed to determine the current directory");
  45. let configuration_dir = base_path.join("configuration");
  46. settings.merge(config::File::from(configuration_dir.join("base")).required(true))?;
  47. let environment: Environment = std::env::var("APP_ENVIRONMENT")
  48. .unwrap_or_else(|_| "local".into())
  49. .try_into()
  50. .expect("Failed to parse APP_ENVIRONMENT.");
  51. settings
  52. .merge(config::File::from(configuration_dir.join(environment.as_str())).required(true))?;
  53. // Add in settings from environment variables (with a prefix of APP and '__' as
  54. // separator) E.g. `APP_APPLICATION__PORT=5001 would set
  55. // `Settings.application.port`
  56. settings.merge(config::Environment::with_prefix("app").separator("__"))?;
  57. settings.try_into()
  58. }
  59. /// The possible runtime environment for our application.
  60. pub enum Environment {
  61. Local,
  62. Production,
  63. }
  64. impl Environment {
  65. pub fn as_str(&self) -> &'static str {
  66. match self {
  67. Environment::Local => "local",
  68. Environment::Production => "production",
  69. }
  70. }
  71. }
  72. impl TryFrom<String> for Environment {
  73. type Error = String;
  74. fn try_from(s: String) -> Result<Self, Self::Error> {
  75. match s.to_lowercase().as_str() {
  76. "local" => Ok(Self::Local),
  77. "production" => Ok(Self::Production),
  78. other => Err(format!(
  79. "{} is not a supported environment. Use either `local` or `production`.",
  80. other
  81. )),
  82. }
  83. }
  84. }