playdate_device/
retry.rs

1//! Retry utils
2
3use std::time::Duration;
4
5
6#[derive(Clone)]
7pub struct Retries<Iters: IterTime = DefaultIterTime> {
8	/// How many iterations to perform before giving up.
9	pub iters: Iters,
10	/// Total awaiting time
11	pub total: Duration,
12}
13
14impl<Iters: IterTime> Retries<Iters> {
15	pub fn new(iters: Iters, total: Duration) -> Self { Self { iters, total } }
16}
17
18impl<T> Default for Retries<T> where T: Default + IterTime {
19	fn default() -> Self {
20		Self { iters: Default::default(),
21		       total: Duration::from_secs(10) }
22	}
23}
24
25impl<T> std::fmt::Display for Retries<T> where T: std::fmt::Display + IterTime {
26	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27		write!(f, "({} => {:?})", self.iters, self.total)
28	}
29}
30impl<T> std::fmt::Debug for Retries<T> where T: std::fmt::Debug + IterTime {
31	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32		write!(f, "({:?} => {:?})", self.iters, self.total)
33	}
34}
35
36
37pub trait IterTime {
38	fn preferred_iter_time(&self) -> Duration;
39
40	#[inline(always)]
41	fn interval(&self, total_wait: &Duration) -> Duration
42		where for<'t> &'t Self: IterTime {
43		calc_interval(total_wait, self)
44	}
45}
46
47
48impl<T: IterTime> IterTime for &'_ T {
49	#[inline(always)]
50	fn preferred_iter_time(&self) -> Duration { T::preferred_iter_time(*self) }
51
52	#[inline(always)]
53	fn interval(&self, total_wait: &Duration) -> Duration
54		where for<'t> &'t Self: IterTime {
55		T::interval(*self, total_wait)
56	}
57}
58
59pub fn calc_interval<T: IterTime>(wait: &Duration, cfg: T) -> Duration {
60	let iters = wait.as_millis() / cfg.preferred_iter_time().as_millis();
61	Duration::from_millis((wait.as_millis() / iters) as _)
62}
63
64
65#[derive(Clone, Default)]
66pub struct DefaultIterTime;
67const MIN_ITER_TIME: u64 = 100;
68
69impl IterTime for DefaultIterTime {
70	fn preferred_iter_time(&self) -> Duration { Duration::from_millis(MIN_ITER_TIME) }
71}
72
73impl std::fmt::Display for DefaultIterTime {
74	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}ms", MIN_ITER_TIME) }
75}
76impl std::fmt::Debug for DefaultIterTime {
77	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78		Duration::from_millis(MIN_ITER_TIME).fmt(f)
79	}
80}
81
82
83impl IterTime for Duration {
84	fn preferred_iter_time(&self) -> Duration { *self }
85
86	fn interval(&self, total_wait: &Duration) -> Duration
87		where for<'t> &'t Self: IterTime {
88		calc_interval(total_wait, self)
89	}
90}
91
92
93pub fn retry_blocking<F, R, E>(retry: Retries<impl IterTime>, f: F) -> Result<R, crate::error::Error>
94	where F: Fn() -> Result<R, E>,
95	      E: Into<crate::error::Error> {
96	let total = &retry.total;
97	let iteration = retry.iters.interval(total);
98	let retries_num = total.as_millis() / iteration.as_millis();
99	trace!("start retries: {retries_num} * {iteration:?} ≈ {total:?}.");
100
101	let mut counter = retries_num;
102	loop {
103		trace!("try: {}/{retries_num}", retries_num - counter);
104		match f() {
105			Ok(r) => return Ok(r),
106			Err(e) => {
107				counter -= 1;
108				if counter == 0 {
109					return Err(e.into());
110				}
111				std::thread::sleep(iteration);
112			},
113		}
114	}
115}