rusty_common/websocket/
reconnect.rs1use parking_lot::Mutex;
6use rand::Rng;
7use std::time::Duration;
8
9pub struct ReconnectStrategy {
11 config: super::ReconnectConfig,
13
14 state: Mutex<ReconnectState>,
16}
17
18struct ReconnectState {
20 attempts: u32,
22
23 current_delay: Duration,
25}
26
27impl ReconnectStrategy {
28 #[must_use]
30 pub const fn new(config: super::ReconnectConfig) -> Self {
31 let initial_delay = config.initial_delay;
32 Self {
33 config,
34 state: Mutex::new(ReconnectState {
35 attempts: 0,
36 current_delay: initial_delay,
37 }),
38 }
39 }
40
41 pub fn should_reconnect(&self) -> bool {
43 if !self.config.enabled {
44 return false;
45 }
46
47 let state = self.state.lock();
48 self.config.max_attempts == 0 || state.attempts < self.config.max_attempts
49 }
50
51 pub fn next_delay(&self) -> Option<Duration> {
53 if !self.should_reconnect() {
54 return None;
55 }
56
57 let mut state = self.state.lock();
58 state.attempts += 1;
59
60 let mut delay = state.current_delay;
62
63 if self.config.jitter {
65 let mut rng = rand::rng();
66 let jitter_range = delay.as_millis() as f64 * 0.1; let jitter = rng.random_range(-jitter_range..=jitter_range) as i64;
68 let millis = (delay.as_millis() as i64 + jitter).max(0) as u64;
69 delay = Duration::from_millis(millis);
70 }
71
72 let next_delay_ms =
74 (state.current_delay.as_millis() as f64 * self.config.multiplier) as u64;
75 state.current_delay =
76 Duration::from_millis(next_delay_ms.min(self.config.max_delay.as_millis() as u64));
77
78 Some(delay)
79 }
80
81 pub fn reset(&self) {
83 let mut state = self.state.lock();
84 state.attempts = 0;
85 state.current_delay = self.config.initial_delay;
86 }
87
88 pub fn attempts(&self) -> u32 {
90 self.state.lock().attempts
91 }
92
93 pub const fn mark_failure(&self) {
95 }
98
99 pub fn exhausted(&self) -> bool {
101 if !self.config.enabled || self.config.max_attempts == 0 {
102 return false;
103 }
104
105 let state = self.state.lock();
106 state.attempts >= self.config.max_attempts
107 }
108}