1use std::time::Duration;
4
5pub struct Backoff {
7 current: Duration,
8 max: Duration,
9 base: Duration,
10}
11
12impl Backoff {
13 pub fn new() -> Self {
15 Self {
16 current: Duration::from_secs(1),
17 max: Duration::from_secs(60),
18 base: Duration::from_secs(1),
19 }
20 }
21
22 pub fn next_delay(&mut self) -> Duration {
24 let duration = self.current;
25 self.current = (self.current * 2).min(self.max);
26 duration
27 }
28
29 pub fn reset(&mut self) {
31 self.current = self.base;
32 }
33
34 pub fn current_ms(&self) -> u64 {
36 self.current.as_millis() as u64
37 }
38}
39
40impl Default for Backoff {
41 fn default() -> Self {
42 Self::new()
43 }
44}
45
46#[cfg(test)]
47mod tests {
48 use super::*;
49
50 #[test]
51 fn backoff_doubles() {
52 let mut b = Backoff::new();
53 assert_eq!(b.next_delay(), Duration::from_secs(1));
54 assert_eq!(b.next_delay(), Duration::from_secs(2));
55 assert_eq!(b.next_delay(), Duration::from_secs(4));
56 assert_eq!(b.next_delay(), Duration::from_secs(8));
57 }
58
59 #[test]
60 fn backoff_caps_at_60s() {
61 let mut b = Backoff::new();
62 for _ in 0..20 {
63 b.next_delay();
64 }
65 assert_eq!(b.next_delay(), Duration::from_secs(60));
66 }
67
68 #[test]
69 fn backoff_resets() {
70 let mut b = Backoff::new();
71 b.next_delay();
72 b.next_delay();
73 b.reset();
74 assert_eq!(b.next_delay(), Duration::from_secs(1));
75 }
76
77 #[test]
78 fn current_ms_reflects_state() {
79 let mut b = Backoff::new();
80 assert_eq!(b.current_ms(), 1000);
81 b.next_delay();
82 assert_eq!(b.current_ms(), 2000);
83 }
84
85 #[test]
86 fn default_is_same_as_new() {
87 let b = Backoff::default();
88 assert_eq!(b.current_ms(), 1000);
89 }
90}