server_env_config/
db.rs

1//! The [`DbConfig`] struct represents settings used to establish a connection with a database.
2
3use crate::env::Environment;
4use crate::{env_bool, env_parsable};
5use anyhow::{Context, Result};
6use std::env;
7use std::time::Duration;
8
9/// Settings used to establish a connection with a database, regardless of the engine.
10/// All the values can be initialized with [`DbConfig::init_for()`] method, that uses
11/// environment variables to setup all of them, otherwise all have default values,
12/// except the string connection.
13#[derive(Debug, Clone)]
14pub struct DbConfig {
15    /// Database URL, initialized with the `DATABASE_URL` env
16    pub database_url: String,
17    /// Min connections created at start-up, value set with `MIN_CONNECTIONS` env,
18    /// default 1
19    pub min_connections: u32,
20    /// Max connections allowed, value set with `MAX_CONNECTIONS` env,
21    /// default 10
22    pub max_connections: u32,
23    /// Time allowed to acquire a connection, value set with `ACQUIRE_TIMEOUT_MS` env,
24    /// default 750 milliseconds
25    pub acquire_timeout: Duration,
26    /// Max time a connection can be idle, value set with `IDLE_TIMEOUT_SEC` env,
27    /// default 300 sec (5 min).
28    /// Any connection that remains in the idle queue longer than this will be closed.
29    pub idle_timeout: Duration,
30    /// Whether to test before test the connection at start-up or not,
31    /// value set with `TEST_BEFORE_ACQUIRE` env, default to false
32    pub test_before_acquire: bool,
33}
34
35impl DbConfig {
36    /// Init the object with `env` passed, and the rest of the
37    /// attributes reading its corresponding environment variable,
38    /// otherwise use a default value.
39    ///
40    /// The database string is saved in `self.database_url` with the value found at
41    /// the `DATABASE_URL` environment value, that it's the only one required (there
42    /// is no default value). If `env` passed is [`Environment::Test`] the prefix
43    /// `_test` is added to the string connection, to avoid using by mistake prod/local
44    /// databases, unless the string already ends with the prefix, or the string has
45    /// connection arguments (the `?` symbol in the string).
46    ///
47    /// # Examples
48    /// ```
49    /// use std::env;
50    /// use server_env_config::db::DbConfig;
51    /// use server_env_config::env::Environment;
52    ///
53    /// // Configurations should be actually set by the OS environment
54    /// env::set_var("DATABASE_URL", "postgresql://user:pass@localhost/db");
55    /// env::set_var("MAX_CONNECTIONS", "50");
56    /// env::set_var("IDLE_TIMEOUT_SEC", "60");
57    ///
58    /// let db = DbConfig::init_for(&Environment::Local).unwrap();
59    ///
60    /// assert_eq!(db.database_url, "postgresql://user:pass@localhost/db");
61    /// assert_eq!(db.max_connections, 50);
62    /// // All settings except DATABASE_URL have default values if env variables are not set
63    /// assert_eq!(db.min_connections, 1);
64    /// assert!(!db.test_before_acquire);
65    ///
66    /// env::remove_var("DATABASE_URL"); // if not set, DbConfig cannot be initialized
67    /// let db = DbConfig::init_for(&Environment::Local);
68    /// assert!(db.is_err());
69    /// ```
70    pub fn init_for(env: &Environment) -> Result<Self> {
71        let url = env::var("DATABASE_URL").context("DATABASE_URL must be set")?;
72        let database_url = if *env == Environment::Test && !url.ends_with("_test") && !url.contains('?') {
73            format!("{url}_test")
74        } else {
75            url
76        };
77        let min_connections = env_parsable::<u32>("MIN_CONNECTIONS", 1)?;
78        let max_connections = env_parsable::<u32>("MAX_CONNECTIONS", 10)?;
79        let acquire_timeout = Duration::from_millis(env_parsable::<u64>("ACQUIRE_TIMEOUT_MS", 750)?);
80        let idle_timeout = Duration::from_secs(env_parsable::<u64>("IDLE_TIMEOUT_SEC", 300)?);
81        let test_before_acquire = env_bool("TEST_BEFORE_ACQUIRE", false)?;
82        Ok(DbConfig {
83            database_url,
84            min_connections,
85            max_connections,
86            acquire_timeout,
87            idle_timeout,
88            test_before_acquire,
89        })
90    }
91}
92
93impl ToString for DbConfig {
94    /// This `to_string()` implementation prints out all the config
95    /// values in `.env` format, using as key the environment variable
96    /// used to set-up the config, even if the configuration was
97    /// set in another way, e.g. using a default value.
98    fn to_string(&self) -> String {
99        format!(
100r#"DATABASE_URL="{}"
101MIN_CONNECTIONS={}
102MAX_CONNECTIONS={}
103ACQUIRE_TIMEOUT_MS={}
104IDLE_TIMEOUT_SEC={}
105TEST_BEFORE_ACQUIRE={}"#,
106            self.database_url,
107            self.min_connections,
108            self.max_connections,
109            self.acquire_timeout.as_millis(),
110            self.idle_timeout.as_secs(),
111            self.test_before_acquire,
112        )
113    }
114}