backon/backoff/
constant.rs1use core::time::Duration;
2
3use crate::backoff::BackoffBuilder;
4
5#[derive(Debug, Clone, Copy)]
35pub struct ConstantBuilder {
36 delay: Duration,
37 max_times: Option<usize>,
38 jitter: bool,
39 seed: Option<u64>,
40}
41
42impl Default for ConstantBuilder {
43 fn default() -> Self {
44 Self::new()
45 }
46}
47
48impl ConstantBuilder {
49 pub const fn new() -> Self {
51 Self {
52 delay: Duration::from_secs(1),
53 max_times: Some(3),
54 jitter: false,
55 seed: None,
56 }
57 }
58
59 pub const fn with_delay(mut self, delay: Duration) -> Self {
61 self.delay = delay;
62 self
63 }
64
65 pub const fn with_max_times(mut self, max_times: usize) -> Self {
67 self.max_times = Some(max_times);
68 self
69 }
70
71 pub const fn with_jitter(mut self) -> Self {
75 self.jitter = true;
76 self
77 }
78
79 pub fn with_jitter_seed(mut self, seed: u64) -> Self {
81 self.seed = Some(seed);
82 self
83 }
84
85 pub const fn without_max_times(mut self) -> Self {
91 self.max_times = None;
92 self
93 }
94}
95
96impl BackoffBuilder for ConstantBuilder {
97 type Backoff = ConstantBackoff;
98
99 fn build(self) -> Self::Backoff {
100 ConstantBackoff {
101 delay: self.delay,
102 max_times: self.max_times,
103
104 attempts: 0,
105 jitter: self.jitter,
106 rng: if let Some(seed) = self.seed {
107 fastrand::Rng::with_seed(seed)
108 } else {
109 #[cfg(feature = "std")]
110 let rng = fastrand::Rng::new();
111
112 #[cfg(not(feature = "std"))]
113 let rng = fastrand::Rng::with_seed(super::RANDOM_SEED);
114
115 rng
116 },
117 }
118 }
119}
120
121impl BackoffBuilder for &ConstantBuilder {
122 type Backoff = ConstantBackoff;
123
124 fn build(self) -> Self::Backoff {
125 (*self).build()
126 }
127}
128
129#[doc(hidden)]
133#[derive(Debug)]
134pub struct ConstantBackoff {
135 delay: Duration,
136 max_times: Option<usize>,
137
138 attempts: usize,
139 jitter: bool,
140 rng: fastrand::Rng,
141}
142
143impl Iterator for ConstantBackoff {
144 type Item = Duration;
145
146 fn next(&mut self) -> Option<Self::Item> {
147 let mut delay = || match self.jitter {
148 true => self.delay + self.delay.mul_f32(self.rng.f32()),
149 false => self.delay,
150 };
151 match self.max_times {
152 None => Some(delay()),
153 Some(max_times) => {
154 if self.attempts >= max_times {
155 None
156 } else {
157 self.attempts += 1;
158 Some(delay())
159 }
160 }
161 }
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use core::time::Duration;
168
169 #[cfg(target_arch = "wasm32")]
170 use wasm_bindgen_test::wasm_bindgen_test as test;
171
172 use super::*;
173
174 const TEST_BUILDER: ConstantBuilder = ConstantBuilder::new()
175 .with_delay(Duration::from_secs(2))
176 .with_max_times(5)
177 .with_jitter();
178
179 #[test]
180 fn test_constant_default() {
181 let mut it = ConstantBuilder::default().build();
182
183 assert_eq!(Some(Duration::from_secs(1)), it.next());
184 assert_eq!(Some(Duration::from_secs(1)), it.next());
185 assert_eq!(Some(Duration::from_secs(1)), it.next());
186 assert_eq!(None, it.next());
187 }
188
189 #[test]
190 fn test_constant_with_delay() {
191 let mut it = ConstantBuilder::default()
192 .with_delay(Duration::from_secs(2))
193 .build();
194
195 assert_eq!(Some(Duration::from_secs(2)), it.next());
196 assert_eq!(Some(Duration::from_secs(2)), it.next());
197 assert_eq!(Some(Duration::from_secs(2)), it.next());
198 assert_eq!(None, it.next());
199 }
200
201 #[test]
202 fn test_constant_with_times() {
203 let mut it = ConstantBuilder::default().with_max_times(1).build();
204
205 assert_eq!(Some(Duration::from_secs(1)), it.next());
206 assert_eq!(None, it.next());
207 }
208
209 #[test]
210 fn test_constant_with_jitter() {
211 let mut it = ConstantBuilder::default().with_jitter().build();
212
213 let dur = it.next().unwrap();
214 fastrand::seed(7);
215 assert!(dur > Duration::from_secs(1));
216 }
217
218 #[test]
219 fn test_constant_without_max_times() {
220 let mut it = ConstantBuilder::default().without_max_times().build();
221
222 for _ in 0..10_000 {
223 assert_eq!(Some(Duration::from_secs(1)), it.next());
224 }
225 }
226
227 #[allow(clippy::assertions_on_constants)]
229 #[test]
230 fn test_constant_const_builder() {
231 assert_eq!(TEST_BUILDER.delay, Duration::from_secs(2));
232 assert_eq!(TEST_BUILDER.max_times, Some(5));
233 assert!(TEST_BUILDER.jitter);
234 }
235}