use std::error::Error; use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; use std::time::{Duration, Instant}; use std::ops::Deref; use std::sync::Mutex; use futures::{Future, Poll}; use futures::future::{Shared, SharedError, SharedItem}; use tokio_service::Service; pub struct Throttle> { inner: S, duration: Duration, current: Mutex)>> } impl Debug for Throttle where S: Service + Debug, { fn fmt(&self, fmt: &mut Formatter) -> FmtResult { fmt.debug_struct("Throttle") .field("inner", &self.inner) .field("duration", &self.duration) .finish() } } impl> Throttle { pub fn new(service: S, duration: Duration) -> Throttle { Throttle { inner: service, duration, current: Mutex::new(None) } } } impl> Service for Throttle { type Request = (); type Response = ThrottledItem; type Error = ThrottledError; type Future = Throttled; fn call(&self, _: ()) -> Self::Future { let now = Instant::now(); let mut current = self.current.lock().expect("lock poisoned"); if let Some((valid_until, ref shared_future)) = *current { if valid_until > now { if let Some(Ok(_)) = shared_future.peek() { return Throttled(shared_future.clone()); } } } let shared_future = self.inner.call(()).shared(); *current = Some((now + self.duration, shared_future.clone())); Throttled(shared_future) } } pub struct Throttled(Shared); impl Debug for Throttled where F: Future + Debug, F::Item: Debug, F::Error: Debug { fn fmt(&self, fmt: &mut Formatter) -> FmtResult { self.0.fmt(fmt) } } impl Future for Throttled { type Item = ThrottledItem; type Error = ThrottledError; fn poll(&mut self) -> Poll { self.0.poll() .map_err(ThrottledError) .map(|async| async.map(ThrottledItem)) } } #[derive(Debug)] pub struct ThrottledItem(SharedItem); impl Deref for ThrottledItem { type Target = T; fn deref(&self) -> &T { &self.0.deref() } } #[derive(Debug)] pub struct ThrottledError(SharedError); impl Deref for ThrottledError { type Target = E; fn deref(&self) -> &E { &self.0.deref() } } impl Display for ThrottledError where E: Display, { fn fmt(&self, f: &mut Formatter) -> FmtResult { self.0.fmt(f) } } impl Error for ThrottledError where E: Error, { fn description(&self) -> &str { self.0.description() } fn cause(&self) -> Option<&Error> { self.0.cause() } }