Skip to main content

fraiseql_wire/connection/conn/
config.rs

1//! Connection configuration types
2
3use std::collections::HashMap;
4use std::time::Duration;
5use zeroize::Zeroizing;
6
7/// Connection configuration
8///
9/// Stores connection parameters including database, credentials, and optional timeouts.
10/// Use `ConnectionConfig::builder()` for advanced configuration with timeouts and keepalive.
11#[derive(Debug, Clone)]
12pub struct ConnectionConfig {
13    /// Database name
14    pub database: String,
15    /// Username
16    pub user: String,
17    /// Password (optional, zeroed on drop for security)
18    pub password: Option<Zeroizing<String>>,
19    /// Additional connection parameters
20    pub params: HashMap<String, String>,
21    /// TCP connection timeout (default: 10 seconds)
22    pub connect_timeout: Option<Duration>,
23    /// Query statement timeout
24    pub statement_timeout: Option<Duration>,
25    /// TCP keepalive idle interval (default: 5 minutes)
26    pub keepalive_idle: Option<Duration>,
27    /// Application name for Postgres logs (default: "fraiseql-wire")
28    pub application_name: Option<String>,
29    /// Postgres `extra_float_digits` setting
30    pub extra_float_digits: Option<i32>,
31}
32
33impl ConnectionConfig {
34    /// Create new configuration with defaults
35    ///
36    /// # Arguments
37    ///
38    /// * `database` - Database name
39    /// * `user` - Username
40    ///
41    /// # Defaults
42    ///
43    /// - `connect_timeout`: None
44    /// - `statement_timeout`: None
45    /// - `keepalive_idle`: None
46    /// - `application_name`: None
47    /// - `extra_float_digits`: None
48    ///
49    /// For configured timeouts and keepalive, use `builder()` instead.
50    pub fn new(database: impl Into<String>, user: impl Into<String>) -> Self {
51        Self {
52            database: database.into(),
53            user: user.into(),
54            password: None,
55            params: HashMap::new(),
56            connect_timeout: None,
57            statement_timeout: None,
58            keepalive_idle: None,
59            application_name: None,
60            extra_float_digits: None,
61        }
62    }
63
64    /// Create a builder for advanced configuration
65    ///
66    /// Use this to configure timeouts, keepalive, and application name.
67    ///
68    /// # Examples
69    ///
70    /// ```rust
71    /// use fraiseql_wire::connection::ConnectionConfig;
72    /// use std::time::Duration;
73    /// let config = ConnectionConfig::builder("mydb", "user")
74    ///     .connect_timeout(Duration::from_secs(10))
75    ///     .statement_timeout(Duration::from_secs(30))
76    ///     .build();
77    /// ```
78    pub fn builder(
79        database: impl Into<String>,
80        user: impl Into<String>,
81    ) -> ConnectionConfigBuilder {
82        ConnectionConfigBuilder {
83            database: database.into(),
84            user: user.into(),
85            password: None,
86            params: HashMap::new(),
87            connect_timeout: None,
88            statement_timeout: None,
89            keepalive_idle: None,
90            application_name: None,
91            extra_float_digits: None,
92        }
93    }
94
95    /// Set password (automatically zeroed on drop)
96    pub fn password(mut self, password: impl Into<String>) -> Self {
97        self.password = Some(Zeroizing::new(password.into()));
98        self
99    }
100
101    /// Add connection parameter
102    pub fn param(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
103        self.params.insert(key.into(), value.into());
104        self
105    }
106}
107
108/// Builder for creating `ConnectionConfig` with advanced options
109///
110/// Provides a fluent API for configuring timeouts, keepalive, and application name.
111///
112/// # Examples
113///
114/// ```rust
115/// use fraiseql_wire::connection::ConnectionConfig;
116/// use std::time::Duration;
117/// let config = ConnectionConfig::builder("mydb", "user")
118///     .password("secret")
119///     .connect_timeout(Duration::from_secs(10))
120///     .statement_timeout(Duration::from_secs(30))
121///     .keepalive_idle(Duration::from_secs(300))
122///     .application_name("my_app")
123///     .build();
124/// ```
125#[must_use = "call .build() to construct the final value"]
126#[derive(Debug, Clone)]
127pub struct ConnectionConfigBuilder {
128    pub(super) database: String,
129    pub(super) user: String,
130    pub(super) password: Option<Zeroizing<String>>,
131    pub(super) params: HashMap<String, String>,
132    pub(super) connect_timeout: Option<Duration>,
133    pub(super) statement_timeout: Option<Duration>,
134    pub(super) keepalive_idle: Option<Duration>,
135    pub(super) application_name: Option<String>,
136    pub(super) extra_float_digits: Option<i32>,
137}
138
139impl ConnectionConfigBuilder {
140    /// Set the password (automatically zeroed on drop)
141    pub fn password(mut self, password: impl Into<String>) -> Self {
142        self.password = Some(Zeroizing::new(password.into()));
143        self
144    }
145
146    /// Add a connection parameter
147    pub fn param(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
148        self.params.insert(key.into(), value.into());
149        self
150    }
151
152    /// Set TCP connection timeout
153    ///
154    /// Default: None (no timeout)
155    ///
156    /// # Arguments
157    ///
158    /// * `duration` - Timeout duration for establishing TCP connection
159    pub const fn connect_timeout(mut self, duration: Duration) -> Self {
160        self.connect_timeout = Some(duration);
161        self
162    }
163
164    /// Set statement (query) timeout
165    ///
166    /// Default: None (unlimited)
167    ///
168    /// # Arguments
169    ///
170    /// * `duration` - Timeout duration for query execution
171    pub const fn statement_timeout(mut self, duration: Duration) -> Self {
172        self.statement_timeout = Some(duration);
173        self
174    }
175
176    /// Set TCP keepalive idle interval
177    ///
178    /// Default: None (OS default)
179    ///
180    /// # Arguments
181    ///
182    /// * `duration` - Idle duration before sending keepalive probes
183    pub const fn keepalive_idle(mut self, duration: Duration) -> Self {
184        self.keepalive_idle = Some(duration);
185        self
186    }
187
188    /// Set application name for Postgres logs
189    ///
190    /// Default: None (Postgres will not set `application_name`)
191    ///
192    /// # Arguments
193    ///
194    /// * `name` - Application name to identify in Postgres logs
195    pub fn application_name(mut self, name: impl Into<String>) -> Self {
196        self.application_name = Some(name.into());
197        self
198    }
199
200    /// Set `extra_float_digits` for float precision
201    ///
202    /// Default: None (use Postgres default)
203    ///
204    /// # Arguments
205    ///
206    /// * `digits` - Number of extra digits (typically 0-2)
207    pub const fn extra_float_digits(mut self, digits: i32) -> Self {
208        self.extra_float_digits = Some(digits);
209        self
210    }
211
212    /// Build the configuration
213    pub fn build(self) -> ConnectionConfig {
214        ConnectionConfig {
215            database: self.database,
216            user: self.user,
217            password: self.password,
218            params: self.params,
219            connect_timeout: self.connect_timeout,
220            statement_timeout: self.statement_timeout,
221            keepalive_idle: self.keepalive_idle,
222            application_name: self.application_name,
223            extra_float_digits: self.extra_float_digits,
224        }
225    }
226}