1use crate::transport::{TransportError, TransportSink, TransportStream};
8use alloc::boxed::Box;
9use core::future::Future;
10use core::pin::Pin;
11use core::time::Duration;
12
13pub trait Reconnector: Send + Sync + 'static {
17 #[allow(clippy::type_complexity)]
18 fn connect<'a>(
19 &'a self,
20 ) -> Pin<
21 Box<
22 dyn Future<
23 Output = Result<
24 (Box<dyn TransportSink>, Box<dyn TransportStream>),
25 TransportError,
26 >,
27 > + Send
28 + 'a,
29 >,
30 >;
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub struct ReconnectPolicy {
39 pub initial_delay: Duration,
40 pub max_delay: Duration,
41 pub multiplier: u32,
42}
43
44impl Default for ReconnectPolicy {
45 fn default() -> Self {
46 Self {
47 initial_delay: Duration::from_secs(1),
48 max_delay: Duration::from_secs(60),
49 multiplier: 2,
50 }
51 }
52}
53
54impl ReconnectPolicy {
55 pub(crate) fn delay_for(&self, attempt: u32) -> Duration {
58 let mut delay = self.initial_delay;
59 for _ in 0..attempt {
60 delay = match delay.checked_mul(self.multiplier) {
61 Some(d) if d < self.max_delay => d,
62 _ => return self.max_delay,
63 };
64 }
65 delay
66 }
67}
68
69#[derive(Debug, Clone, Copy)]
74pub enum ReconnectBehavior {
75 Enabled(ReconnectPolicy),
76 Disabled,
77}
78
79impl Default for ReconnectBehavior {
80 fn default() -> Self {
81 Self::Enabled(ReconnectPolicy::default())
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88
89 #[test]
90 fn delay_doubles_and_caps() {
91 let policy = ReconnectPolicy {
92 initial_delay: Duration::from_secs(1),
93 max_delay: Duration::from_secs(10),
94 multiplier: 2,
95 };
96 assert_eq!(policy.delay_for(0), Duration::from_secs(1));
97 assert_eq!(policy.delay_for(1), Duration::from_secs(2));
98 assert_eq!(policy.delay_for(2), Duration::from_secs(4));
99 assert_eq!(policy.delay_for(3), Duration::from_secs(8));
100 assert_eq!(policy.delay_for(4), Duration::from_secs(10));
101 assert_eq!(policy.delay_for(10), Duration::from_secs(10));
102 }
103}