use crate::util::serde::default_true;
use serde_derive::{Deserialize, Serialize};
use serde_with::serde_as;
use std::time::Duration;
use url::Url;
use validator::Validate;
#[serde_as]
#[derive(Debug, Clone, Validate, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub struct Database {
pub uri: Url,
pub auto_migrate: bool,
#[serde(default = "Database::default_connect_timeout")]
#[serde_as(as = "serde_with::DurationMilliSeconds")]
pub connect_timeout: Duration,
#[serde(default = "default_true")]
pub connect_lazy: bool,
#[serde(default = "Database::default_acquire_timeout")]
#[serde_as(as = "serde_with::DurationMilliSeconds")]
pub acquire_timeout: Duration,
#[serde_as(as = "Option<serde_with::DurationSeconds>")]
pub idle_timeout: Option<Duration>,
#[serde_as(as = "Option<serde_with::DurationSeconds>")]
pub max_lifetime: Option<Duration>,
#[serde(default)]
pub min_connections: u32,
pub max_connections: u32,
#[serde(default = "default_true")]
pub test_on_checkout: bool,
#[cfg(feature = "db-diesel-pool-async")]
#[serde(default = "default_true")]
pub retry_connection: bool,
#[serde(default)]
pub temporary_test_db: bool,
#[serde(default = "default_true")]
pub temporary_test_db_clean_up: bool,
#[cfg(feature = "test-containers")]
#[serde(default)]
#[validate(nested)]
pub test_container: Option<crate::config::TestContainer>,
}
impl Database {
fn default_connect_timeout() -> Duration {
Duration::from_millis(1000)
}
fn default_acquire_timeout() -> Duration {
Duration::from_millis(1000)
}
}
#[cfg(feature = "db-sea-orm")]
impl From<Database> for sea_orm::ConnectOptions {
fn from(database: Database) -> Self {
sea_orm::ConnectOptions::from(&database)
}
}
#[cfg(feature = "db-sea-orm")]
impl From<&Database> for sea_orm::ConnectOptions {
fn from(database: &Database) -> Self {
let mut options = sea_orm::ConnectOptions::new(database.uri.to_string());
options
.test_before_acquire(database.test_on_checkout)
.connect_timeout(database.connect_timeout)
.connect_lazy(database.connect_lazy)
.acquire_timeout(database.acquire_timeout)
.min_connections(database.min_connections)
.max_connections(database.max_connections)
.sqlx_logging(false);
if let Some(idle_timeout) = database.idle_timeout {
options.idle_timeout(idle_timeout);
}
if let Some(max_lifetime) = database.max_lifetime {
options.max_lifetime(max_lifetime);
}
options
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::testing::snapshot::TestCase;
use rstest::fixture;
#[fixture]
#[cfg_attr(coverage_nightly, coverage(off))]
fn case() -> TestCase {
Default::default()
}
#[rstest::rstest]
#[case(
r#"
uri = "https://example.com:1234"
auto-migrate = true
max-connections = 1
"#
)]
#[case(
r#"
uri = "https://example.com:1234"
auto-migrate = true
max-connections = 1
connect-timeout = 1000
acquire-timeout = 2000
idle-timeout = 3000
max-lifetime = 4000
"#
)]
#[cfg(feature = "db-diesel-pool-async")]
#[cfg_attr(coverage_nightly, coverage(off))]
fn serialization(_case: TestCase, #[case] config: &str) {
let database: Database = toml::from_str(config).unwrap();
insta::assert_toml_snapshot!(database);
}
#[fixture]
#[cfg_attr(coverage_nightly, coverage(off))]
fn db_config() -> Database {
Database {
uri: Url::parse("postgres://example:example@example:1234/example_app").unwrap(),
#[cfg(feature = "test-containers")]
test_container: None,
auto_migrate: true,
connect_timeout: Duration::from_secs(1),
connect_lazy: true,
acquire_timeout: Duration::from_secs(2),
idle_timeout: Some(Duration::from_secs(3)),
max_lifetime: Some(Duration::from_secs(4)),
min_connections: 10,
max_connections: 20,
test_on_checkout: true,
#[cfg(feature = "db-diesel-pool-async")]
retry_connection: true,
temporary_test_db: false,
temporary_test_db_clean_up: false,
}
}
#[rstest::rstest]
#[cfg(feature = "db-sea-orm")]
#[cfg_attr(coverage_nightly, coverage(off))]
fn db_config_to_connect_options(db_config: Database) {
let connect_options = sea_orm::ConnectOptions::from(db_config);
insta::assert_debug_snapshot!(connect_options);
}
#[rstest::rstest]
#[cfg(feature = "db-sea-orm")]
#[cfg_attr(coverage_nightly, coverage(off))]
fn db_config_to_connect_options_ref(db_config: Database) {
let connect_options = sea_orm::ConnectOptions::from(&db_config);
insta::assert_debug_snapshot!(connect_options);
}
}