Skip to main content

scosh_core/
recovery.rs

1//! Finite recovery budget shared by desktop and mobile adapters.
2
3use 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}