1use std::time::Duration;
6
7#[derive(Debug, Clone)]
8pub struct RetryPolicy {
9 pub max_attempts: u32,
11 pub base_delay: Duration,
12 pub max_delay: Duration,
13}
14
15impl Default for RetryPolicy {
16 fn default() -> Self {
17 Self {
18 max_attempts: 3,
19 base_delay: Duration::from_millis(200),
20 max_delay: Duration::from_secs(5),
21 }
22 }
23}
24
25impl RetryPolicy {
26 pub fn none() -> Self {
28 Self {
29 max_attempts: 1,
30 ..Self::default()
31 }
32 }
33
34 pub fn delay_for(&self, attempt: u32, retry_after: Option<Duration>) -> Duration {
37 if let Some(ra) = retry_after {
38 return ra.min(self.max_delay);
39 }
40 self.base_delay
41 .saturating_mul(2u32.saturating_pow(attempt))
42 .min(self.max_delay)
43 }
44}
45
46#[cfg(test)]
47mod tests {
48 use super::*;
49
50 #[test]
51 fn backoff_doubles_and_caps() {
52 let p = RetryPolicy::default();
53 assert_eq!(p.delay_for(0, None), Duration::from_millis(200));
54 assert_eq!(p.delay_for(1, None), Duration::from_millis(400));
55 assert_eq!(p.delay_for(2, None), Duration::from_millis(800));
56 assert_eq!(p.delay_for(30, None), Duration::from_secs(5)); }
58
59 #[test]
60 fn retry_after_overrides_but_caps() {
61 let p = RetryPolicy::default();
62 assert_eq!(
63 p.delay_for(0, Some(Duration::from_secs(1))),
64 Duration::from_secs(1)
65 );
66 assert_eq!(
67 p.delay_for(0, Some(Duration::from_secs(60))),
68 Duration::from_secs(5)
69 );
70 }
71
72 #[test]
73 fn none_means_single_attempt() {
74 assert_eq!(RetryPolicy::none().max_attempts, 1);
75 }
76}