exponential_backoff/
into_iter.rs

1use super::Backoff;
2use fastrand::Rng;
3use std::{iter, time::Duration};
4
5/// An exponential backoff iterator.
6#[derive(Debug, Clone)]
7pub struct IntoIter {
8    inner: Backoff,
9    rng: Rng,
10    attempts: u32,
11}
12
13impl IntoIter {
14    pub(crate) fn new(inner: Backoff) -> Self {
15        Self {
16            attempts: 0,
17            rng: Rng::new(),
18            inner,
19        }
20    }
21}
22
23impl iter::Iterator for IntoIter {
24    type Item = Option<Duration>;
25
26    #[inline]
27    fn next(&mut self) -> Option<Self::Item> {
28        // Check whether we've exceeded the number of attempts,
29        // or whether we're on our last attempt. We don't want to sleep after
30        // the last attempt.
31        if self.attempts == self.inner.max_attempts {
32            return None;
33        } else if self.attempts == self.inner.max_attempts - 1 {
34            self.attempts = self.attempts.saturating_add(1);
35            return Some(None);
36        }
37
38        // Create exponential duration.
39        let exponent = self.inner.factor.saturating_pow(self.attempts);
40        let mut duration = self.inner.min.saturating_mul(exponent);
41
42        // Increment the attempts counter.
43        self.attempts = self.attempts.saturating_add(1);
44
45        // Apply jitter. Uses multiples of 100 to prevent relying on floats.
46        //
47        // We put this in a conditional block because the `fastrand` crate
48        // doesn't like `0..0` inputs, and dividing by zero is also not a good
49        // idea.
50        if self.inner.jitter != 0.0 {
51            let jitter_factor = (self.inner.jitter * 100f32) as u32;
52            let random = self.rng.u32(0..jitter_factor * 2);
53            let mut duration = duration.saturating_mul(100);
54            if random < jitter_factor {
55                let jitter = duration.saturating_mul(random) / 100;
56                duration = duration.saturating_sub(jitter);
57            } else {
58                let jitter = duration.saturating_mul(random / 2) / 100;
59                duration = duration.saturating_add(jitter);
60            };
61            duration /= 100;
62        }
63
64        // Make sure it doesn't exceed upper / lower bounds.
65        duration = duration.clamp(self.inner.min, self.inner.max);
66
67        Some(Some(duration))
68    }
69}