use std::sync::{Arc, Mutex};
use std::time::{Duration, SystemTime};
pub trait Clock: Send + Sync + 'static {
fn now(&self) -> SystemTime;
}
impl<C: Clock> Clock for Arc<C> {
fn now(&self) -> SystemTime {
C::now(self)
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct SystemClock;
impl Clock for SystemClock {
fn now(&self) -> SystemTime {
SystemTime::now()
}
}
#[derive(Clone, Copy, Debug)]
pub struct TokioClock {
system_base: SystemTime,
tokio_base: tokio::time::Instant,
}
impl TokioClock {
pub fn new() -> Self {
Self {
system_base: SystemTime::now(),
tokio_base: tokio::time::Instant::now(),
}
}
}
impl Default for TokioClock {
fn default() -> Self {
Self::new()
}
}
impl Clock for TokioClock {
fn now(&self) -> SystemTime {
self.system_base + self.tokio_base.elapsed()
}
}
#[derive(Debug)]
pub struct ManualClock {
now: Mutex<SystemTime>,
}
impl ManualClock {
pub const fn new(start: SystemTime) -> Self {
Self {
now: Mutex::new(start),
}
}
pub fn advance(&self, by: Duration) {
let mut now = self
.now
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
*now += by;
}
pub fn set(&self, to: SystemTime) {
*self
.now
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) = to;
}
}
impl Default for ManualClock {
fn default() -> Self {
Self::new(SystemTime::UNIX_EPOCH + Duration::from_secs(1_700_000_000))
}
}
impl Clock for ManualClock {
fn now(&self) -> SystemTime {
*self
.now
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test(start_paused = true)]
async fn tokio_clock_follows_paused_time() {
let clock = TokioClock::new();
let start = clock.now();
tokio::time::sleep(Duration::from_secs(90)).await;
assert_eq!(clock.now(), start + Duration::from_secs(90));
}
#[test]
fn manual_clock_only_moves_when_told() {
let clock = ManualClock::default();
let start = clock.now();
assert_eq!(clock.now(), start);
clock.advance(Duration::from_secs(30));
assert_eq!(clock.now(), start + Duration::from_secs(30));
}
}