mssql_client/config/
types.rs1use std::time::Duration;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
14pub enum ApplicationIntent {
15 #[default]
17 ReadWrite,
18 ReadOnly,
20}
21
22#[derive(Debug, Clone)]
27pub struct RedirectConfig {
28 pub max_redirects: u8,
30 pub follow_redirects: bool,
32}
33
34impl Default for RedirectConfig {
35 fn default() -> Self {
36 Self {
37 max_redirects: 2,
38 follow_redirects: true,
39 }
40 }
41}
42
43impl RedirectConfig {
44 #[must_use]
46 pub fn new() -> Self {
47 Self::default()
48 }
49
50 #[must_use]
52 pub fn max_redirects(mut self, max: u8) -> Self {
53 self.max_redirects = max;
54 self
55 }
56
57 #[must_use]
59 pub fn follow_redirects(mut self, follow: bool) -> Self {
60 self.follow_redirects = follow;
61 self
62 }
63
64 #[must_use]
69 pub fn no_follow() -> Self {
70 Self {
71 max_redirects: 0,
72 follow_redirects: false,
73 }
74 }
75}
76
77#[derive(Debug, Clone)]
82pub struct TimeoutConfig {
83 pub connect_timeout: Duration,
85 pub tls_timeout: Duration,
87 pub login_timeout: Duration,
89 pub command_timeout: Duration,
91 pub idle_timeout: Duration,
93 pub keepalive_interval: Option<Duration>,
95}
96
97impl Default for TimeoutConfig {
98 fn default() -> Self {
99 Self {
100 connect_timeout: Duration::from_secs(15),
101 tls_timeout: Duration::from_secs(10),
102 login_timeout: Duration::from_secs(30),
103 command_timeout: Duration::from_secs(30),
104 idle_timeout: Duration::from_secs(300),
105 keepalive_interval: Some(Duration::from_secs(30)),
106 }
107 }
108}
109
110impl TimeoutConfig {
111 #[must_use]
113 pub fn new() -> Self {
114 Self::default()
115 }
116
117 #[must_use]
119 pub fn connect_timeout(mut self, timeout: Duration) -> Self {
120 self.connect_timeout = timeout;
121 self
122 }
123
124 #[must_use]
126 pub fn tls_timeout(mut self, timeout: Duration) -> Self {
127 self.tls_timeout = timeout;
128 self
129 }
130
131 #[must_use]
133 pub fn login_timeout(mut self, timeout: Duration) -> Self {
134 self.login_timeout = timeout;
135 self
136 }
137
138 #[must_use]
140 pub fn command_timeout(mut self, timeout: Duration) -> Self {
141 self.command_timeout = timeout;
142 self
143 }
144
145 #[must_use]
147 pub fn idle_timeout(mut self, timeout: Duration) -> Self {
148 self.idle_timeout = timeout;
149 self
150 }
151
152 #[must_use]
154 pub fn keepalive_interval(mut self, interval: Option<Duration>) -> Self {
155 self.keepalive_interval = interval;
156 self
157 }
158
159 #[must_use]
161 pub fn no_keepalive(mut self) -> Self {
162 self.keepalive_interval = None;
163 self
164 }
165
166 #[must_use]
168 pub fn total_connect_timeout(&self) -> Duration {
169 self.connect_timeout + self.tls_timeout + self.login_timeout
170 }
171}
172
173#[derive(Debug, Clone)]
178pub struct RetryPolicy {
179 pub max_retries: u32,
181 pub initial_backoff: Duration,
183 pub max_backoff: Duration,
185 pub backoff_multiplier: f64,
187 pub jitter: bool,
189}
190
191impl Default for RetryPolicy {
192 fn default() -> Self {
193 Self {
194 max_retries: 3,
195 initial_backoff: Duration::from_millis(100),
196 max_backoff: Duration::from_secs(30),
197 backoff_multiplier: 2.0,
198 jitter: true,
199 }
200 }
201}
202
203impl RetryPolicy {
204 #[must_use]
206 pub fn new() -> Self {
207 Self::default()
208 }
209
210 #[must_use]
212 pub fn max_retries(mut self, max: u32) -> Self {
213 self.max_retries = max;
214 self
215 }
216
217 #[must_use]
219 pub fn initial_backoff(mut self, backoff: Duration) -> Self {
220 self.initial_backoff = backoff;
221 self
222 }
223
224 #[must_use]
226 pub fn max_backoff(mut self, backoff: Duration) -> Self {
227 self.max_backoff = backoff;
228 self
229 }
230
231 #[must_use]
233 pub fn backoff_multiplier(mut self, multiplier: f64) -> Self {
234 self.backoff_multiplier = multiplier;
235 self
236 }
237
238 #[must_use]
240 pub fn jitter(mut self, enabled: bool) -> Self {
241 self.jitter = enabled;
242 self
243 }
244
245 #[must_use]
247 pub fn no_retry() -> Self {
248 Self {
249 max_retries: 0,
250 ..Self::default()
251 }
252 }
253
254 #[must_use]
258 pub fn backoff_for_attempt(&self, attempt: u32) -> Duration {
259 if attempt == 0 {
260 return Duration::ZERO;
261 }
262
263 let base = self.initial_backoff.as_millis() as f64
264 * self
265 .backoff_multiplier
266 .powi(attempt.saturating_sub(1) as i32);
267 let capped = base.min(self.max_backoff.as_millis() as f64);
268
269 if self.jitter {
270 Duration::from_millis(capped as u64)
273 } else {
274 Duration::from_millis(capped as u64)
275 }
276 }
277
278 #[must_use]
280 pub fn should_retry(&self, attempt: u32) -> bool {
281 attempt < self.max_retries
282 }
283}