use crate::{ErrorHandler, RetryPolicy};
use futures::{Async, Future, Poll};
use std::time::Instant;
use tokio_timer;
pub trait FutureFactory {
type FutureItem: Future;
fn new(&mut self) -> Self::FutureItem;
}
impl<T, F> FutureFactory for T
where
T: FnMut() -> F,
F: Future,
{
type FutureItem = F;
#[allow(clippy::new_ret_no_self)]
fn new(&mut self) -> F {
(*self)()
}
}
pub struct FutureRetry<F, R>
where
F: FutureFactory,
{
factory: F,
error_action: R,
state: RetryState<F::FutureItem>,
}
enum RetryState<F> {
WaitingForFuture(F),
TimerActive(tokio_timer::Delay),
}
impl<F: FutureFactory, R> FutureRetry<F, R> {
pub fn new(mut factory: F, error_action: R) -> Self {
let current_future = factory.new();
Self {
factory,
error_action,
state: RetryState::WaitingForFuture(current_future),
}
}
}
impl<F: FutureFactory, R> Future for FutureRetry<F, R>
where
R: ErrorHandler<<F::FutureItem as Future>::Error>,
{
type Item = <<F as FutureFactory>::FutureItem as Future>::Item;
type Error = R::OutError;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
loop {
let new_state = match self.state {
RetryState::TimerActive(ref mut delay) => match delay.poll() {
Ok(Async::Ready(())) => RetryState::WaitingForFuture(self.factory.new()),
Ok(Async::NotReady) => return Ok(Async::NotReady),
Err(e) => {
panic!("Timer error: {}", e)
}
},
RetryState::WaitingForFuture(ref mut future) => match future.poll() {
Ok(x) => {
self.error_action.ok();
return Ok(x);
}
Err(e) => match self.error_action.handle(e) {
RetryPolicy::ForwardError(e) => return Err(e),
RetryPolicy::Repeat => RetryState::WaitingForFuture(self.factory.new()),
RetryPolicy::WaitRetry(duration) => RetryState::TimerActive(
tokio_timer::Delay::new(Instant::now() + duration),
),
},
},
};
self.state = new_state;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use futures::future::{err, ok};
use std::time::Duration;
use tokio;
struct FutureIterator<F>(F);
impl<I, F> FutureFactory for FutureIterator<I>
where
I: Iterator<Item = F>,
F: Future,
{
type FutureItem = F;
fn new(&mut self) -> Self::FutureItem {
self.0.next().expect("No more futures!")
}
}
#[test]
fn naive() {
let f = FutureRetry::new(|| ok::<_, u8>(1u8), |_| RetryPolicy::Repeat::<u8>);
assert_eq!(Ok(1u8), f.wait());
}
#[test]
fn naive_error_forward() {
let f = FutureRetry::new(|| err::<u8, _>(1u8), RetryPolicy::ForwardError);
assert_eq!(Err(1u8), f.wait());
}
#[test]
fn more_complicated_wait() {
let f = FutureRetry::new(FutureIterator(vec![err(2u8), ok(3u8)].into_iter()), |_| {
RetryPolicy::WaitRetry::<u8>(Duration::from_millis(10))
})
.then(|x| {
assert_eq!(Ok(3u8), x);
Ok(())
});
tokio::run(f);
}
#[test]
fn more_complicated_repeat() {
let f = FutureRetry::new(FutureIterator(vec![err(2u8), ok(3u8)].into_iter()), |_| {
RetryPolicy::Repeat::<u8>
});
assert_eq!(Ok(3u8), f.wait());
}
#[test]
fn more_complicated_forward() {
let f = FutureRetry::new(
FutureIterator(vec![err(2u8), ok(3u8)].into_iter()),
RetryPolicy::ForwardError,
);
assert_eq!(Err(2u8), f.wait());
}
}