mod running_cheat_clock;
mod stable_cheat_clock;
mod true_clock;
use std::{fmt, future::Future, pin::Pin, time::Duration};
use anyhow::Error;
use chrono::{DateTime, TimeDelta, Utc};
use tokio::{select, sync::Notify, time::sleep};
pub use {
running_cheat_clock::{RunningCheatClock, RunningCheatClockFactory, RunningCheatClockRemote},
stable_cheat_clock::{StableCheatClock, StableCheatClockFactory, StableCheatClockRemote},
true_clock::TrueClock,
};
pub trait ClockBase: Send + Sync {
fn now(&self) -> DateTime<Utc>;
}
pub trait Clock: Send + Sync + fmt::Debug + ClockBase {
fn sleep(&self, delay: TimeDelta) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>;
fn sleep_until(&self, deadline: DateTime<Utc>)
-> Pin<Box<dyn Future<Output = ()> + Send + '_>>;
fn clone_box(&self) -> Box<dyn Clock>;
fn synchronize(&self);
}
#[cfg(test)]
pub const REMOTE_REFRESH_RATE: Duration = Duration::from_millis(10);
#[cfg(not(test))]
pub const REMOTE_REFRESH_RATE: Duration = Duration::from_secs(3);
pub const SAFETY_REFRESH_DELAY: Duration = Duration::from_secs(120);
pub trait CheatClockRemote: ClockBase {
#[cfg(test)]
fn not_blocking(&self);
fn remote_now(&self) -> DateTime<Utc>;
fn advance_toward(&self, date: DateTime<Utc>);
fn notify(&self) -> &Notify;
fn is_sync(&self) -> bool;
fn wait_for_the_clock(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
Box::pin(async move {
loop {
if self.is_sync() {
break;
}
select! {
_ = sleep(REMOTE_REFRESH_RATE) => {},
_ = self.notify().notified() => {}
}
}
})
}
fn clone_box(&self) -> Box<dyn CheatClockRemote>;
}
#[allow(clippy::type_complexity)]
pub trait CheatClockFactory {
fn get_clock(
&self,
date: DateTime<Utc>,
) -> Result<(Box<dyn CheatClockRemote>, Box<dyn Clock>), Error>;
}