1use std::time::Duration;
4
5#[derive(Debug, Clone)]
19pub struct Config {
20 pub(crate) max_size: usize,
21 pub(crate) min_idle: usize,
22 pub(crate) acquire_timeout: Option<Duration>,
23 pub(crate) max_lifetime: Option<Duration>,
24 pub(crate) idle_timeout: Option<Duration>,
25 pub(crate) max_uses: Option<u64>,
26 pub(crate) reap_interval: Duration,
27}
28
29impl Default for Config {
30 fn default() -> Self {
31 Self {
32 max_size: 16,
33 min_idle: 0,
34 acquire_timeout: Some(Duration::from_secs(30)),
35 max_lifetime: Some(Duration::from_secs(30 * 60)),
36 idle_timeout: Some(Duration::from_secs(10 * 60)),
37 max_uses: None,
38 reap_interval: Duration::from_secs(30),
39 }
40 }
41}
42
43impl Config {
44 pub fn new() -> Self {
46 Self::default()
47 }
48
49 pub fn max_size(mut self, n: usize) -> Self {
51 assert!(n > 0, "max_size must be greater than zero");
52 self.max_size = n;
53 self
54 }
55
56 pub fn min_idle(mut self, n: usize) -> Self {
65 self.min_idle = n;
66 self
67 }
68
69 pub fn acquire_timeout(mut self, t: impl Into<Option<Duration>>) -> Self {
72 self.acquire_timeout = t.into();
73 self
74 }
75
76 pub fn max_lifetime(mut self, t: impl Into<Option<Duration>>) -> Self {
78 self.max_lifetime = t.into();
79 self
80 }
81
82 pub fn idle_timeout(mut self, t: impl Into<Option<Duration>>) -> Self {
84 self.idle_timeout = t.into();
85 self
86 }
87
88 pub fn max_uses(mut self, n: impl Into<Option<u64>>) -> Self {
90 self.max_uses = n.into();
91 self
92 }
93
94 pub fn reap_interval(mut self, t: Duration) -> Self {
100 assert!(!t.is_zero(), "reap_interval must be non-zero");
101 self.reap_interval = t;
102 self
103 }
104
105 pub(crate) fn needs_reaper(&self) -> bool {
106 self.min_idle > 0 || self.idle_timeout.is_some() || self.max_lifetime.is_some()
107 }
108
109 pub(crate) fn effective_min_idle(&self) -> usize {
110 self.min_idle.min(self.max_size)
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117
118 fn untimed() -> Config {
120 Config::new().max_lifetime(None).idle_timeout(None)
121 }
122
123 #[test]
124 fn defaults_are_documented_values() {
125 let c = Config::default();
126 assert_eq!(c.max_size, 16);
127 assert_eq!(c.min_idle, 0);
128 assert_eq!(c.acquire_timeout, Some(Duration::from_secs(30)));
129 assert_eq!(c.max_lifetime, Some(Duration::from_secs(30 * 60)));
130 assert_eq!(c.idle_timeout, Some(Duration::from_secs(10 * 60)));
131 assert_eq!(c.max_uses, None);
132 assert_eq!(c.reap_interval, Duration::from_secs(30));
133 }
134
135 #[test]
136 fn new_is_default() {
137 let a = Config::new();
138 let b = Config::default();
139 assert_eq!(a.max_size, b.max_size);
140 assert_eq!(a.min_idle, b.min_idle);
141 assert_eq!(a.acquire_timeout, b.acquire_timeout);
142 assert_eq!(a.max_lifetime, b.max_lifetime);
143 assert_eq!(a.idle_timeout, b.idle_timeout);
144 assert_eq!(a.max_uses, b.max_uses);
145 assert_eq!(a.reap_interval, b.reap_interval);
146 }
147
148 #[test]
149 fn every_setter_round_trips() {
150 let c = Config::new()
151 .max_size(7)
152 .min_idle(3)
153 .acquire_timeout(Duration::from_millis(11))
154 .max_lifetime(Duration::from_millis(22))
155 .idle_timeout(Duration::from_millis(33))
156 .max_uses(44u64)
157 .reap_interval(Duration::from_millis(55));
158
159 assert_eq!(c.max_size, 7);
160 assert_eq!(c.min_idle, 3);
161 assert_eq!(c.acquire_timeout, Some(Duration::from_millis(11)));
162 assert_eq!(c.max_lifetime, Some(Duration::from_millis(22)));
163 assert_eq!(c.idle_timeout, Some(Duration::from_millis(33)));
164 assert_eq!(c.max_uses, Some(44));
165 assert_eq!(c.reap_interval, Duration::from_millis(55));
166 }
167
168 #[test]
171 fn optional_setters_accept_none() {
172 let c = Config::new()
173 .acquire_timeout(None)
174 .max_lifetime(None)
175 .idle_timeout(None)
176 .max_uses(None);
177
178 assert_eq!(c.acquire_timeout, None);
179 assert_eq!(c.max_lifetime, None);
180 assert_eq!(c.idle_timeout, None);
181 assert_eq!(c.max_uses, None);
182 }
183
184 #[test]
185 fn optional_setters_accept_an_explicit_some() {
186 let c = Config::new()
187 .acquire_timeout(Some(Duration::from_secs(1)))
188 .max_lifetime(Some(Duration::from_secs(2)))
189 .idle_timeout(Some(Duration::from_secs(3)))
190 .max_uses(Some(4u64));
191
192 assert_eq!(c.acquire_timeout, Some(Duration::from_secs(1)));
193 assert_eq!(c.max_lifetime, Some(Duration::from_secs(2)));
194 assert_eq!(c.idle_timeout, Some(Duration::from_secs(3)));
195 assert_eq!(c.max_uses, Some(4));
196 }
197
198 #[test]
199 #[should_panic(expected = "max_size must be greater than zero")]
200 fn a_zero_max_size_panics() {
201 let _ = Config::new().max_size(0);
203 }
204
205 #[test]
206 #[should_panic(expected = "reap_interval must be non-zero")]
207 fn a_zero_reap_interval_panics() {
208 let _ = Config::new().reap_interval(Duration::ZERO);
210 }
211
212 #[test]
213 fn min_idle_is_stored_unclamped_but_read_back_clamped() {
214 let c = Config::new().max_size(2).min_idle(9);
215 assert_eq!(c.min_idle, 9, "the setter records what was asked for");
216 assert_eq!(c.effective_min_idle(), 2, "but never exceeds max_size");
217 }
218
219 #[test]
220 fn effective_min_idle_passes_through_below_max_size() {
221 assert_eq!(
222 Config::new().max_size(9).min_idle(2).effective_min_idle(),
223 2
224 );
225 assert_eq!(
226 Config::new().max_size(9).min_idle(0).effective_min_idle(),
227 0
228 );
229 assert_eq!(
230 Config::new().max_size(4).min_idle(4).effective_min_idle(),
231 4
232 );
233 }
234
235 #[test]
236 fn a_config_with_no_timed_policy_needs_no_reaper() {
237 assert!(!untimed().needs_reaper());
238 }
239
240 #[test]
241 fn each_timed_policy_on_its_own_needs_a_reaper() {
242 assert!(
243 untimed().min_idle(1).needs_reaper(),
244 "min_idle has to be refilled by someone"
245 );
246 assert!(
247 untimed()
248 .idle_timeout(Duration::from_secs(1))
249 .needs_reaper(),
250 "an idle connection nobody asks for ages out only in the reaper"
251 );
252 assert!(
253 untimed()
254 .max_lifetime(Duration::from_secs(1))
255 .needs_reaper(),
256 "same for the hard age cap"
257 );
258 }
259
260 #[test]
263 fn max_uses_alone_needs_no_reaper() {
264 assert!(!untimed().max_uses(1u64).needs_reaper());
265 }
266
267 #[test]
268 fn is_clone_and_debug() {
269 let c = Config::new().max_size(3).min_idle(1);
270 let d = c.clone();
271 assert_eq!(d.max_size, 3);
272 assert_eq!(d.min_idle, 1);
273 assert!(format!("{c:?}").contains("max_size"));
274 }
275}