1use std::time::{Duration, Instant};
4
5use crate::errors::SdkError;
6
7#[derive(Clone, Copy, Debug, Eq, PartialEq)]
8pub struct RecoveryPolicy {
9 pub max_attempts: u8,
10 pub budget: Duration,
11}
12
13impl Default for RecoveryPolicy {
14 fn default() -> Self {
15 Self {
16 max_attempts: 5,
17 budget: Duration::from_secs(30),
18 }
19 }
20}
21
22#[derive(Clone, Copy, Debug, Eq, PartialEq)]
23pub struct RecoveryBudget {
24 policy: RecoveryPolicy,
25 started_at: Option<Instant>,
26 attempts: u8,
27}
28
29impl RecoveryBudget {
30 pub const fn new(policy: RecoveryPolicy) -> Self {
31 Self {
32 policy,
33 started_at: None,
34 attempts: 0,
35 }
36 }
37
38 pub const fn attempts(self) -> u8 {
39 self.attempts
40 }
41
42 pub fn begin(&mut self, now: Instant) {
43 self.started_at = Some(now);
44 self.attempts = 0;
45 }
46
47 pub fn next_attempt(&mut self, now: Instant) -> Result<u8, SdkError> {
48 let started = self.started_at.get_or_insert(now);
49 if self.attempts >= self.policy.max_attempts
50 || now.duration_since(*started) >= self.policy.budget
51 {
52 return Err(SdkError::RecoveryExhausted);
53 }
54 self.attempts = self.attempts.saturating_add(1);
55 Ok(self.attempts)
56 }
57
58 pub fn reset(&mut self) {
59 self.started_at = None;
60 self.attempts = 0;
61 }
62}