pub mod retry {
use crate::tarantool::fiber::sleep;
use std::time::Duration;
use thiserror::Error;
#[derive(Debug, Error)]
#[error("Retry error. Error: {source}, delay: {total_delay:?}, tries: {tries}")]
pub struct RetryError<E> {
pub source: E,
total_delay: Duration,
tries: u64,
}
#[allow(dead_code)]
pub enum OpResult<T, E> {
Ok(T),
Retry(E),
Stop(E),
}
impl<T, E> From<std::result::Result<T, E>> for OpResult<T, E> {
fn from(r: std::result::Result<T, E>) -> OpResult<T, E> {
match r {
Ok(r) => OpResult::Ok(r),
Err(e) => OpResult::Retry(e),
}
}
}
pub fn retry<I, O, T, E, R>(mut iterable: I, mut op: O) -> Result<T, RetryError<E>>
where
I: Iterator<Item = Duration>,
R: Into<OpResult<T, E>>,
O: FnMut() -> R,
{
let mut total_delay = Duration::default();
let mut tries = 1;
loop {
let rv = op().into();
match rv {
OpResult::Ok(value) => return Ok(value),
OpResult::Retry(err) => {
if let Some(delay) = iterable.next() {
sleep(delay);
total_delay += delay;
tries += 1;
} else {
return Err(RetryError {
source: err,
total_delay,
tries,
});
}
}
OpResult::Stop(err) => {
return Err(RetryError {
source: err,
total_delay,
tries,
})
}
};
}
}
#[derive(Debug, Clone, Copy)]
pub struct Fibonacci {
curr: u64,
next: u64,
}
impl Fibonacci {
pub fn from_millis(millis: u64) -> Fibonacci {
Fibonacci {
curr: millis,
next: millis,
}
}
}
impl Iterator for Fibonacci {
type Item = Duration;
fn next(&mut self) -> Option<Duration> {
if let Some(next_next) = self.curr.checked_add(self.next) {
self.curr = self.next;
self.next = next_next;
} else {
self.curr = self.next;
self.next = u64::MAX;
}
let duration = Duration::from_millis(self.curr);
Some(duration)
}
}
#[test]
fn fibonacci() {
let mut iter = Fibonacci::from_millis(10);
assert_eq!(iter.next(), Some(Duration::from_millis(10)));
assert_eq!(iter.next(), Some(Duration::from_millis(20)));
assert_eq!(iter.next(), Some(Duration::from_millis(30)));
assert_eq!(iter.next(), Some(Duration::from_millis(50)));
assert_eq!(iter.next(), Some(Duration::from_millis(80)));
}
}