Files
renderbug-bike/src/backoff.rs

81 lines
2.1 KiB
Rust

use embassy_time::{Duration, Timer};
use log::warn;
#[derive(Clone, Copy, Debug)]
pub struct Backoff {
delay: Duration,
attempts: Attempts,
}
#[derive(Clone, Copy, Debug)]
pub enum Attempts {
Finite(u64),
Forever
}
impl Iterator for Attempts {
type Item = Self;
fn next(&mut self) -> Option<Self::Item> {
match *self {
Attempts::Forever => Some(Attempts::Forever),
Attempts::Finite(0) => None,
Attempts::Finite(n) => {
*self = Attempts::Finite(n - 1);
Some(Attempts::Finite(n))
}
}
}
}
impl Backoff {
pub fn from_millis(millis: u64) -> Self {
Self {
delay: Duration::from_millis(millis),
attempts: Attempts::Finite(3),
}
}
pub fn from_secs(secs: u64) -> Self {
Self {
delay: Duration::from_secs(secs),
attempts: Attempts::Finite(3),
}
}
pub fn forever(self) -> Self {
Self {
attempts: Attempts::Forever,
..self
}
}
pub async fn attempt<F: AsyncFnMut() -> Result<T, E>, T, E: core::fmt::Debug>(self, mut f: F) -> Result<T, E> {
let mut latest= f().await;
let mut delay = self.delay;
'outer: loop {
for attempt in self.attempts {
match &latest {
Err(err) => {
match attempt {
Attempts::Finite(1) => {
warn!("Operation failed on final attempt: {err:?}");
break 'outer
}
attempt => {
warn!("Operation failed: {err:?} Retrying attempt {attempt:?} after {delay}");
Timer::after(delay).await;
delay *= 2;
latest = f().await
}
}
},
_ => break 'outer
}
}
}
latest
}
}