atomr_remote_serial/
reconnect.rs1use std::time::Duration;
9
10#[derive(Debug, Clone)]
13pub struct ReconnectPolicy {
14 pub initial: Duration,
16 pub max: Duration,
18 pub multiplier: f64,
20}
21
22impl Default for ReconnectPolicy {
23 fn default() -> Self {
24 Self { initial: Duration::from_millis(50), max: Duration::from_secs(5), multiplier: 2.0 }
25 }
26}
27
28impl ReconnectPolicy {
29 pub fn never() -> Self {
32 Self { initial: Duration::ZERO, max: Duration::ZERO, multiplier: 1.0 }
33 }
34
35 pub(crate) fn next_delay(&self, current: Duration) -> Duration {
36 if self.max.is_zero() {
37 return Duration::ZERO;
38 }
39 let scaled = current.mul_f64(self.multiplier);
40 scaled.min(self.max)
41 }
42
43 pub(crate) fn is_enabled(&self) -> bool {
44 !self.max.is_zero()
45 }
46}
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51
52 #[test]
53 fn default_grows_to_cap_then_holds() {
54 let p = ReconnectPolicy::default();
55 let mut d = p.initial;
56 for _ in 0..10 {
57 d = p.next_delay(d);
58 }
59 assert_eq!(d, p.max, "exponential backoff should saturate at `max`");
60 }
61
62 #[test]
63 fn never_disables_reconnect() {
64 let p = ReconnectPolicy::never();
65 assert!(!p.is_enabled());
66 assert_eq!(p.next_delay(Duration::from_secs(1)), Duration::ZERO);
67 }
68
69 #[test]
70 fn next_delay_uses_multiplier() {
71 let p = ReconnectPolicy {
72 initial: Duration::from_millis(100),
73 max: Duration::from_secs(10),
74 multiplier: 3.0,
75 };
76 assert_eq!(p.next_delay(Duration::from_millis(100)), Duration::from_millis(300));
77 assert_eq!(p.next_delay(Duration::from_millis(300)), Duration::from_millis(900));
78 }
79}