81 lines
2.1 KiB
Rust
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
|
|
}
|
|
}
|