1use std::time::Duration;
4
5#[derive(Clone, Copy, Debug, Eq, PartialEq)]
7pub struct ExponentialSchedule {
8 initial: Duration,
9 maximum: Duration,
10 multiplier: u32,
11 current: Duration,
12}
13
14impl ExponentialSchedule {
15 #[must_use]
17 pub fn new(initial: Duration, maximum: Duration, multiplier: u32) -> Self {
18 Self {
19 initial,
20 maximum,
21 multiplier: multiplier.max(1),
22 current: initial.min(maximum),
23 }
24 }
25
26 pub fn next_delay(&mut self) -> Duration {
28 let delay = self.current;
29
30 self.current = self
31 .current
32 .checked_mul(self.multiplier)
33 .unwrap_or(self.maximum)
34 .min(self.maximum);
35
36 delay
37 }
38
39 pub fn reset(&mut self) {
41 self.current = self.initial.min(self.maximum);
42 }
43
44 #[must_use]
46 pub const fn current_delay(&self) -> Duration {
47 self.current
48 }
49}
50
51#[cfg(test)]
52mod tests {
53 use googletest::prelude::*;
54
55 use super::*;
56
57 #[gtest]
58 fn grows_until_maximum_and_resets() -> Result<()> {
59 let mut schedule =
60 ExponentialSchedule::new(Duration::from_millis(100), Duration::from_millis(500), 2);
61
62 verify_that!(schedule.next_delay(), eq(Duration::from_millis(100)))?;
63 verify_that!(schedule.next_delay(), eq(Duration::from_millis(200)))?;
64 verify_that!(schedule.next_delay(), eq(Duration::from_millis(400)))?;
65 verify_that!(schedule.next_delay(), eq(Duration::from_millis(500)))?;
66 verify_that!(schedule.next_delay(), eq(Duration::from_millis(500)))?;
67
68 schedule.reset();
69
70 verify_that!(schedule.current_delay(), eq(Duration::from_millis(100)))
71 }
72
73 #[gtest]
74 fn normalizes_degenerate_bounds_and_multiplier() -> Result<()> {
75 let mut schedule =
76 ExponentialSchedule::new(Duration::from_secs(2), Duration::from_secs(1), 0);
77
78 verify_that!(schedule.next_delay(), eq(Duration::from_secs(1)))?;
79
80 verify_that!(schedule.next_delay(), eq(Duration::from_secs(1)))
81 }
82}