use crate::{ErrorHandler, RetryPolicy};
use futures::{Async, AsyncSink, Future, Poll, Sink, StartSend};
use std::time::Instant;
use tokio_timer;
pub struct SinkRetry<F, S> {
error_action: F,
sink: S,
state: RetryState,
}
impl<F, S> SinkRetry<F, S>
where
S: Sink,
S::SinkItem: Clone,
F: ErrorHandler<S::SinkError>,
F::OutError: From<S::SinkError>,
{
pub fn new(sink: S, error_action: F) -> Self
where
S: Sink,
{
Self {
error_action,
sink,
state: RetryState::WaitingForSink,
}
}
fn try_send_item(&mut self, item: S::SinkItem) -> StartSend<S::SinkItem, F::OutError> {
debug_assert!(self.state.is_waiting_for_sink());
loop {
let cloned_item = item.clone();
match self.sink.start_send(cloned_item) {
Err(err) => {
match self.error_action.handle(err) {
RetryPolicy::Repeat => continue,
RetryPolicy::WaitRetry(duration) => {
let mut timer = tokio_timer::Delay::new(Instant::now() + duration);
match timer.poll().expect("Timer panic!") {
Async::Ready(_) => match self.poll_complete()? {
Async::Ready(()) => continue,
Async::NotReady => {}
},
Async::NotReady => {}
}
self.state = RetryState::TimerActive(timer);
return Ok(AsyncSink::NotReady(item));
}
RetryPolicy::ForwardError(err) => return Err(err),
}
}
Ok(ok) => return Ok(ok),
}
}
}
}
pub trait SinkRetryExt: Sink {
fn retry<F>(self, error_action: F) -> SinkRetry<F, Self>
where
Self: Sized,
F: ErrorHandler<Self::SinkError>,
Self::SinkItem: Clone,
F: ErrorHandler<Self::SinkError>,
F::OutError: From<Self::SinkError>,
{
SinkRetry::new(self, error_action)
}
}
impl<S: ?Sized> SinkRetryExt for S where S: Sink {}
enum RetryState {
WaitingForSink,
TimerActive(tokio_timer::Delay),
}
impl RetryState {
#[inline]
fn is_waiting_for_sink(&self) -> bool {
match self {
RetryState::WaitingForSink => true,
_ => false,
}
}
}
impl<F, S> Sink for SinkRetry<F, S>
where
S: Sink,
S::SinkItem: Clone,
F: ErrorHandler<S::SinkError>,
F::OutError: From<S::SinkError>,
{
type SinkItem = S::SinkItem;
type SinkError = F::OutError;
fn start_send(&mut self, item: Self::SinkItem) -> StartSend<Self::SinkItem, Self::SinkError> {
match self.state {
RetryState::WaitingForSink => self.try_send_item(item),
RetryState::TimerActive(ref mut timer) => match timer.poll().expect("Timer panic!") {
Async::NotReady => Ok(AsyncSink::NotReady(item)),
Async::Ready(()) => self.try_send_item(item),
},
}
}
fn poll_complete(&mut self) -> Poll<(), Self::SinkError> {
loop {
let new_state = match self.state {
RetryState::TimerActive(ref mut delay) => match delay.poll() {
Ok(Async::Ready(())) => RetryState::WaitingForSink,
Ok(Async::NotReady) => return Ok(Async::NotReady),
Err(e) => {
panic!("Timer error: {}", e)
}
},
RetryState::WaitingForSink => match self.sink.poll_complete() {
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::WaitingForSink,
RetryPolicy::WaitRetry(duration) => RetryState::TimerActive(
tokio_timer::Delay::new(Instant::now() + duration),
),
},
},
};
self.state = new_state;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::marker::PhantomData;
enum SinkReturn {
ReadyToFlush,
NotReadyToFlush,
}
struct SinkIterResultMock<T, I> {
iter: I,
_sink_item: PhantomData<T>,
}
fn iter_result<T, J, E>(i: J) -> SinkIterResultMock<T, J::IntoIter>
where
J: IntoIterator<Item = Result<SinkReturn, E>>,
{
SinkIterResultMock {
iter: i.into_iter(),
_sink_item: PhantomData,
}
}
impl<T, I, E> Sink for SinkIterResultMock<T, I>
where
I: Iterator<Item = Result<SinkReturn, E>>,
{
type SinkItem = T;
type SinkError = E;
fn start_send(
&mut self,
item: Self::SinkItem,
) -> StartSend<Self::SinkItem, Self::SinkError> {
match self.poll_complete()? {
Async::Ready(()) => Ok(AsyncSink::Ready),
Async::NotReady => Ok(AsyncSink::NotReady(item)),
}
}
fn poll_complete(&mut self) -> Poll<(), E> {
match self.iter.next().expect("Iterator called after done!")? {
SinkReturn::ReadyToFlush => Ok(Async::Ready(())),
SinkReturn::NotReadyToFlush => Ok(Async::NotReady),
}
}
}
#[test]
fn get_item_when_error_is_handling() {
let sink = iter_result(vec![
Ok(SinkReturn::NotReadyToFlush),
Err(17u8),
Ok(SinkReturn::NotReadyToFlush),
Ok(SinkReturn::NotReadyToFlush),
]);
let mut retry = sink.retry(|_| RetryPolicy::Repeat::<u8>);
assert_eq!(Ok(AsyncSink::NotReady(5)), retry.start_send(5));
assert_eq!(Ok(AsyncSink::NotReady(7)), retry.start_send(7));
assert_eq!(Ok(AsyncSink::NotReady(8)), retry.start_send(8));
}
#[test]
fn repeat() {
let sink = iter_result::<u8, _, _>(vec![
Ok(SinkReturn::ReadyToFlush),
Err(17u64),
Ok(SinkReturn::ReadyToFlush),
Ok(SinkReturn::NotReadyToFlush),
]);
let mut retry = SinkRetry::new(sink, |_| RetryPolicy::Repeat::<u64>);
assert_eq!(Ok(AsyncSink::Ready), retry.start_send(2));
assert_eq!(Ok(AsyncSink::Ready), retry.start_send(2));
assert_eq!(Ok(AsyncSink::NotReady(2)), retry.start_send(2));
}
#[test]
fn propagate() {
let sink = iter_result::<u8, _, _>(vec![Err(17u8), Ok(SinkReturn::ReadyToFlush)]);
let mut retry = SinkRetry::new(sink, RetryPolicy::ForwardError);
assert_eq!(Err(17u8), retry.start_send(3));
assert_eq!(Ok(AsyncSink::Ready), retry.start_send(3));
}
}