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 { 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 Result, T, E: core::fmt::Debug>(self, mut f: F) -> Result { 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 } }