use std::sync::Mutex;
use std::time::Instant;
pub trait Clock: Send + Sync {
fn monotonic(&self) -> f64;
fn sleep(&self, seconds: f64);
#[cfg(feature = "tokio")]
fn sleep_async<'a>(
&'a self,
seconds: f64,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + Send + 'a>>;
}
#[derive(Debug)]
pub struct SystemClock {
start: Instant,
}
impl SystemClock {
pub fn new() -> Self {
Self {
start: Instant::now(),
}
}
}
impl Default for SystemClock {
fn default() -> Self {
Self::new()
}
}
impl Clock for SystemClock {
fn monotonic(&self) -> f64 {
self.start.elapsed().as_secs_f64()
}
fn sleep(&self, seconds: f64) {
if seconds > 0.0 {
std::thread::sleep(std::time::Duration::from_secs_f64(seconds));
}
}
#[cfg(feature = "tokio")]
fn sleep_async<'a>(
&'a self,
seconds: f64,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + Send + 'a>> {
Box::pin(async move {
if seconds > 0.0 {
tokio::time::sleep(std::time::Duration::from_secs_f64(seconds)).await;
}
})
}
}
#[derive(Debug)]
pub struct ManualClock {
now: Mutex<f64>,
}
impl ManualClock {
pub fn new(start: f64) -> Self {
Self {
now: Mutex::new(start),
}
}
pub fn advance(&self, seconds: f64) {
*self.now.lock().unwrap() += seconds;
}
}
impl Clock for ManualClock {
fn monotonic(&self) -> f64 {
*self.now.lock().unwrap()
}
fn sleep(&self, seconds: f64) {
self.advance(seconds);
}
#[cfg(feature = "tokio")]
fn sleep_async<'a>(
&'a self,
seconds: f64,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + Send + 'a>> {
self.advance(seconds);
Box::pin(async {})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn system_clock_advances_and_sleeps() {
let c = SystemClock::new();
let t0 = c.monotonic();
assert!(t0 >= 0.0);
c.sleep(0.0); c.sleep(-1.0); c.sleep(0.01);
assert!(c.monotonic() >= t0);
let _ = SystemClock::default();
assert!(format!("{c:?}").contains("SystemClock"));
}
#[test]
fn manual_clock_is_controllable() {
let c = ManualClock::new(1000.0);
assert_eq!(c.monotonic(), 1000.0);
c.advance(5.0);
assert_eq!(c.monotonic(), 1005.0);
c.sleep(2.5);
assert_eq!(c.monotonic(), 1007.5);
assert!(format!("{c:?}").contains("ManualClock"));
}
#[cfg(feature = "tokio")]
#[tokio::test]
async fn sleep_async_system_and_manual() {
let s = SystemClock::new();
s.sleep_async(0.0).await; s.sleep_async(0.01).await; let m = ManualClock::new(0.0);
m.sleep_async(5.0).await; assert_eq!(m.monotonic(), 5.0);
}
}