roadster/config/database/
mod.rs1use crate::util::serde::default_true;
2use serde_derive::{Deserialize, Serialize};
3use serde_with::serde_as;
4use std::time::Duration;
5use url::Url;
6use validator::Validate;
7
8#[serde_as]
9#[derive(Debug, Clone, Validate, Serialize, Deserialize)]
10#[serde(rename_all = "kebab-case")]
11#[non_exhaustive]
12pub struct Database {
13 pub uri: Url,
15
16 pub auto_migrate: bool,
19
20 #[serde(default = "Database::default_connect_timeout")]
21 #[serde_as(as = "serde_with::DurationMilliSeconds")]
22 pub connect_timeout: Duration,
23
24 #[serde(default = "default_true")]
27 pub connect_lazy: bool,
28
29 #[serde(default = "Database::default_acquire_timeout")]
30 #[serde_as(as = "serde_with::DurationMilliSeconds")]
31 pub acquire_timeout: Duration,
32
33 #[serde_as(as = "Option<serde_with::DurationSeconds>")]
34 pub idle_timeout: Option<Duration>,
35
36 #[serde_as(as = "Option<serde_with::DurationSeconds>")]
37 pub max_lifetime: Option<Duration>,
38
39 #[serde(default)]
40 pub min_connections: u32,
41
42 pub max_connections: u32,
43
44 #[serde(default = "default_true")]
45 pub test_on_checkout: bool,
46
47 #[cfg(feature = "db-diesel-pool-async")]
49 #[serde(default = "default_true")]
50 pub retry_connection: bool,
51
52 #[serde(default)]
54 pub temporary_test_db: bool,
55
56 #[serde(default = "default_true")]
60 pub temporary_test_db_clean_up: bool,
61
62 #[cfg(feature = "test-containers")]
66 #[serde(default)]
67 #[validate(nested)]
68 pub test_container: Option<crate::config::TestContainer>,
69}
70
71impl Database {
72 fn default_connect_timeout() -> Duration {
73 Duration::from_millis(1000)
74 }
75
76 fn default_acquire_timeout() -> Duration {
77 Duration::from_millis(1000)
78 }
79}
80
81#[cfg(feature = "db-sea-orm")]
82impl From<Database> for sea_orm::ConnectOptions {
83 fn from(database: Database) -> Self {
84 sea_orm::ConnectOptions::from(&database)
85 }
86}
87
88#[cfg(feature = "db-sea-orm")]
89impl From<&Database> for sea_orm::ConnectOptions {
90 fn from(database: &Database) -> Self {
91 let mut options = sea_orm::ConnectOptions::new(database.uri.to_string());
92 options
93 .test_before_acquire(database.test_on_checkout)
94 .connect_timeout(database.connect_timeout)
95 .connect_lazy(database.connect_lazy)
96 .acquire_timeout(database.acquire_timeout)
97 .min_connections(database.min_connections)
98 .max_connections(database.max_connections)
99 .sqlx_logging(false);
100 if let Some(idle_timeout) = database.idle_timeout {
101 options.idle_timeout(idle_timeout);
102 }
103 if let Some(max_lifetime) = database.max_lifetime {
104 options.max_lifetime(max_lifetime);
105 }
106 options
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113 use crate::testing::snapshot::TestCase;
114 use rstest::fixture;
115
116 #[fixture]
117 #[cfg_attr(coverage_nightly, coverage(off))]
118 fn case() -> TestCase {
119 Default::default()
120 }
121
122 #[rstest::rstest]
123 #[case(
124 r#"
125 uri = "https://example.com:1234"
126 auto-migrate = true
127 max-connections = 1
128 "#
129 )]
130 #[case(
131 r#"
132 uri = "https://example.com:1234"
133 auto-migrate = true
134 max-connections = 1
135 connect-timeout = 1000
136 acquire-timeout = 2000
137 idle-timeout = 3000
138 max-lifetime = 4000
139 "#
140 )]
141 #[cfg(feature = "db-diesel-pool-async")]
142 #[cfg_attr(coverage_nightly, coverage(off))]
143 fn serialization(_case: TestCase, #[case] config: &str) {
144 let database: Database = toml::from_str(config).unwrap();
145
146 insta::assert_toml_snapshot!(database);
147 }
148
149 #[fixture]
150 #[cfg_attr(coverage_nightly, coverage(off))]
151 fn db_config() -> Database {
152 Database {
153 uri: Url::parse("postgres://example:example@example:1234/example_app").unwrap(),
154 #[cfg(feature = "test-containers")]
155 test_container: None,
156 auto_migrate: true,
157 connect_timeout: Duration::from_secs(1),
158 connect_lazy: true,
159 acquire_timeout: Duration::from_secs(2),
160 idle_timeout: Some(Duration::from_secs(3)),
161 max_lifetime: Some(Duration::from_secs(4)),
162 min_connections: 10,
163 max_connections: 20,
164 test_on_checkout: true,
165 #[cfg(feature = "db-diesel-pool-async")]
166 retry_connection: true,
167 temporary_test_db: false,
168 temporary_test_db_clean_up: false,
169 }
170 }
171
172 #[rstest::rstest]
173 #[cfg(feature = "db-sea-orm")]
174 #[cfg_attr(coverage_nightly, coverage(off))]
175 fn db_config_to_connect_options(db_config: Database) {
176 let connect_options = sea_orm::ConnectOptions::from(db_config);
177
178 insta::assert_debug_snapshot!(connect_options);
179 }
180
181 #[rstest::rstest]
182 #[cfg(feature = "db-sea-orm")]
183 #[cfg_attr(coverage_nightly, coverage(off))]
184 fn db_config_to_connect_options_ref(db_config: Database) {
185 let connect_options = sea_orm::ConnectOptions::from(&db_config);
186
187 insta::assert_debug_snapshot!(connect_options);
188 }
189}