Skip to main content

dbkit_rs/
config.rs

1/// Configuration for a dbkit database connection.
2///
3/// Can be built from a URL string or constructed with the builder.
4///
5/// # Example
6/// ```
7/// use dbkit::DbkitConfig;
8///
9/// // From URL
10/// let config = DbkitConfig::from_url("postgres://localhost/mydb");
11///
12/// // From builder
13/// let config = DbkitConfig::builder()
14///     .host("db.example.com")
15///     .port(5432)
16///     .database("myapp")
17///     .user("admin")
18///     .password("secret")
19///     .pool_size(16)
20///     .connect_timeout_secs(10)
21///     .build();
22/// ```
23#[derive(Debug, Clone)]
24pub struct DbkitConfig {
25    /// Postgres connection URL.
26    pub url: String,
27    /// Maximum pool size. Default: 16.
28    pub pool_size: usize,
29    /// Connection timeout in seconds. Default: 30.
30    pub connect_timeout_secs: u64,
31    /// Idle timeout in seconds. Connections idle longer are reaped. Default: 300.
32    pub idle_timeout_secs: u64,
33    /// Auto-create the database if it doesn't exist. Default: true.
34    pub auto_create_db: bool,
35}
36
37impl DbkitConfig {
38    /// Create config from a connection URL with default settings.
39    pub fn from_url(url: &str) -> Self {
40        Self {
41            url: url.to_string(),
42            pool_size: 16,
43            connect_timeout_secs: 30,
44            idle_timeout_secs: 300,
45            auto_create_db: true,
46        }
47    }
48
49    /// Start building a config from individual connection parameters.
50    pub fn builder() -> ConfigBuilder {
51        ConfigBuilder::default()
52    }
53}
54
55/// Builder for constructing a [`DbkitConfig`] from individual parameters.
56pub struct ConfigBuilder {
57    host: String,
58    port: u16,
59    database: String,
60    user: Option<String>,
61    password: Option<String>,
62    pool_size: usize,
63    connect_timeout_secs: u64,
64    idle_timeout_secs: u64,
65    auto_create_db: bool,
66    ssl_mode: SslMode,
67}
68
69/// SSL mode for Postgres connections.
70#[derive(Debug, Clone, Copy, Default)]
71pub enum SslMode {
72    /// No SSL (default — matches current NoTls behavior).
73    #[default]
74    Disable,
75    /// Prefer SSL but allow fallback.
76    Prefer,
77    /// Require SSL.
78    Require,
79}
80
81impl Default for ConfigBuilder {
82    fn default() -> Self {
83        Self {
84            host: "localhost".into(),
85            port: 5432,
86            database: "postgres".into(),
87            user: None,
88            password: None,
89            pool_size: 16,
90            connect_timeout_secs: 30,
91            idle_timeout_secs: 300,
92            auto_create_db: true,
93            ssl_mode: SslMode::default(),
94        }
95    }
96}
97
98impl ConfigBuilder {
99    pub fn host(mut self, host: &str) -> Self {
100        self.host = host.to_string();
101        self
102    }
103
104    pub fn port(mut self, port: u16) -> Self {
105        self.port = port;
106        self
107    }
108
109    pub fn database(mut self, database: &str) -> Self {
110        self.database = database.to_string();
111        self
112    }
113
114    pub fn user(mut self, user: &str) -> Self {
115        self.user = Some(user.to_string());
116        self
117    }
118
119    pub fn password(mut self, password: &str) -> Self {
120        self.password = Some(password.to_string());
121        self
122    }
123
124    pub fn pool_size(mut self, size: usize) -> Self {
125        self.pool_size = size;
126        self
127    }
128
129    pub fn connect_timeout_secs(mut self, secs: u64) -> Self {
130        self.connect_timeout_secs = secs;
131        self
132    }
133
134    pub fn idle_timeout_secs(mut self, secs: u64) -> Self {
135        self.idle_timeout_secs = secs;
136        self
137    }
138
139    pub fn auto_create_db(mut self, enabled: bool) -> Self {
140        self.auto_create_db = enabled;
141        self
142    }
143
144    pub fn ssl_mode(mut self, mode: SslMode) -> Self {
145        self.ssl_mode = mode;
146        self
147    }
148
149    /// Build the config, constructing the connection URL from parts.
150    pub fn build(self) -> DbkitConfig {
151        let auth = match (&self.user, &self.password) {
152            (Some(u), Some(p)) => format!("{}:{}@", u, p),
153            (Some(u), None) => format!("{}@", u),
154            _ => String::new(),
155        };
156
157        let ssl_param = match self.ssl_mode {
158            SslMode::Disable => "",
159            SslMode::Prefer => "?sslmode=prefer",
160            SslMode::Require => "?sslmode=require",
161        };
162
163        let url = format!(
164            "postgres://{}{}:{}/{}{}",
165            auth, self.host, self.port, self.database, ssl_param
166        );
167
168        DbkitConfig {
169            url,
170            pool_size: self.pool_size,
171            connect_timeout_secs: self.connect_timeout_secs,
172            idle_timeout_secs: self.idle_timeout_secs,
173            auto_create_db: self.auto_create_db,
174        }
175    }
176}
177
178#[cfg(test)]
179mod tests {
180    use super::*;
181
182    #[test]
183    fn test_from_url() {
184        let config = DbkitConfig::from_url("postgres://localhost/mydb");
185        assert_eq!(config.url, "postgres://localhost/mydb");
186        assert_eq!(config.pool_size, 16);
187        assert!(config.auto_create_db);
188    }
189
190    #[test]
191    fn test_builder_full() {
192        let config = DbkitConfig::builder()
193            .host("db.example.com")
194            .port(5433)
195            .database("myapp")
196            .user("admin")
197            .password("secret")
198            .pool_size(32)
199            .connect_timeout_secs(10)
200            .ssl_mode(SslMode::Require)
201            .build();
202
203        assert_eq!(
204            config.url,
205            "postgres://admin:secret@db.example.com:5433/myapp?sslmode=require"
206        );
207        assert_eq!(config.pool_size, 32);
208        assert_eq!(config.connect_timeout_secs, 10);
209    }
210
211    #[test]
212    fn test_builder_minimal() {
213        let config = DbkitConfig::builder().database("test").build();
214        assert_eq!(config.url, "postgres://localhost:5432/test");
215    }
216
217    #[test]
218    fn test_builder_user_no_password() {
219        let config = DbkitConfig::builder()
220            .user("readonly")
221            .database("prod")
222            .build();
223        assert_eq!(config.url, "postgres://readonly@localhost:5432/prod");
224    }
225}