use crate::transport::{TransportError, TransportSink, TransportStream};
use alloc::boxed::Box;
use core::future::Future;
use core::pin::Pin;
use core::time::Duration;
pub trait Reconnector: Send + Sync + 'static {
#[allow(clippy::type_complexity)]
fn connect<'a>(
&'a self,
) -> Pin<
Box<
dyn Future<
Output = Result<
(Box<dyn TransportSink>, Box<dyn TransportStream>),
TransportError,
>,
> + Send
+ 'a,
>,
>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ReconnectPolicy {
pub initial_delay: Duration,
pub max_delay: Duration,
pub multiplier: u32,
}
impl Default for ReconnectPolicy {
fn default() -> Self {
Self {
initial_delay: Duration::from_secs(1),
max_delay: Duration::from_secs(60),
multiplier: 2,
}
}
}
impl ReconnectPolicy {
pub(crate) fn delay_for(&self, attempt: u32) -> Duration {
let mut delay = self.initial_delay;
for _ in 0..attempt {
delay = match delay.checked_mul(self.multiplier) {
Some(d) if d < self.max_delay => d,
_ => return self.max_delay,
};
}
delay
}
}
#[derive(Debug, Clone, Copy)]
pub enum ReconnectBehavior {
Enabled(ReconnectPolicy),
Disabled,
}
impl Default for ReconnectBehavior {
fn default() -> Self {
Self::Enabled(ReconnectPolicy::default())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn delay_doubles_and_caps() {
let policy = ReconnectPolicy {
initial_delay: Duration::from_secs(1),
max_delay: Duration::from_secs(10),
multiplier: 2,
};
assert_eq!(policy.delay_for(0), Duration::from_secs(1));
assert_eq!(policy.delay_for(1), Duration::from_secs(2));
assert_eq!(policy.delay_for(2), Duration::from_secs(4));
assert_eq!(policy.delay_for(3), Duration::from_secs(8));
assert_eq!(policy.delay_for(4), Duration::from_secs(10));
assert_eq!(policy.delay_for(10), Duration::from_secs(10));
}
}