//! Syncronisation helpers use super::*; use futures::{ Future, }; use tokio::time::Duration; /// Force a future to take *at least* a specific amount of time to complete. /// /// If the future takes longer than `time` to complete, no additional delay is added. /// If `time` is zero, no delay is added. /// /// # Panics /// The returned future will panic if /// * The future itself panics /// * We are unable to convert the future's duration from `chrono`'s duration span to std's duration span pub fn timesync<'a, T, F>(future: F, time: Duration) -> impl Future + 'a where F: Future + 'a { use chrono::prelude::*; #[cold] #[inline(never)] fn _panic_failed_tmconv(orig: chrono::Duration, error: impl Into) { panic!("Failed to convert chrono duration span {:?} to std duration span: {}", orig, error.into()); } async move { let now = Utc::now(); let r = future.await; if time.is_zero() { return r; } let after = Utc::now(); let duration = after - now; match duration.to_std() { Ok(taken) if taken < time => { // Delay for the remaining time tokio::time::delay_for(time - taken).await; }, Err(error) => _panic_failed_tmconv(duration, error), // At least `time` has passed _ => (), } r } } pub trait TimeSyncExt<'a>: Future + Sized + 'a { type OutputFuture: Future + 'a; fn pad_time(self, time: Duration) -> Self::OutputFuture; } #[cfg(nightly)] impl<'a, F> TimeSyncExt<'a> for F where F: Future + 'a { //#[cfg(nightly)] type OutputFuture = impl Future + 'a; //#[cfg(not(nightly))] //type OutputFuture = futures::future::BoxFuture<'a, Self::Output>; //TODO: Requires `Send` bound for some reason. fn pad_time(self, time: Duration) -> Self::OutputFuture { /*#[cfg(not(nightly))] { use futures::prelude::*; return timesync(self, time).boxed(); } #[cfg(nightly)] { return*/ timesync(self, time) //} } }