use std::time::{Duration, SystemTime, UNIX_EPOCH};
pub trait Clock {
fn unix_now(&self) -> u64;
fn unix_now_plus(&self, offset: Duration) -> u64 {
self.unix_now().saturating_add(offset.as_secs())
}
}
#[derive(Debug, Default, Clone, Copy)]
pub struct SystemClock;
impl SystemClock {
#[must_use]
pub fn new() -> Self {
Self
}
}
impl Clock for SystemClock {
fn unix_now(&self) -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0)
}
}
#[cfg(any(test, feature = "test-utils"))]
#[derive(Debug, Clone, Copy)]
pub struct FixedClock(pub u64);
#[cfg(any(test, feature = "test-utils"))]
impl FixedClock {
#[must_use]
pub fn at(unix_secs: u64) -> Self {
Self(unix_secs)
}
#[must_use]
pub fn advance(self, secs: u64) -> Self {
Self(self.0.saturating_add(secs))
}
}
#[cfg(any(test, feature = "test-utils"))]
impl Clock for FixedClock {
fn unix_now(&self) -> u64 {
self.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn system_clock_is_positive() {
let t = SystemClock::new().unix_now();
assert!(t > 1_704_067_200, "clock looks wrong: {t}");
}
#[test]
fn fixed_clock_is_deterministic() {
let c = FixedClock::at(1_000_000);
assert_eq!(c.unix_now(), 1_000_000);
assert_eq!(c.unix_now(), 1_000_000);
}
#[test]
fn unix_now_plus_offsets_correctly() {
let c = FixedClock::at(1_000);
assert_eq!(c.unix_now_plus(Duration::from_secs(100)), 1_100);
}
#[test]
fn advance_produces_later_clock() {
let c = FixedClock::at(1_000).advance(60);
assert_eq!(c.unix_now(), 1_060);
}
}