use std::sync::Arc;
use std::time::{Instant, SystemTime, UNIX_EPOCH};
pub type SharedClock = Arc<dyn Clock>;
pub trait Clock: Send + Sync {
fn monotonic_millis(&self) -> u64;
fn epoch_seconds(&self) -> u64;
}
#[derive(Debug)]
pub struct SystemClock {
started_at: Instant,
}
impl Default for SystemClock {
fn default() -> Self {
Self::new()
}
}
impl SystemClock {
#[must_use]
pub fn new() -> Self {
Self {
started_at: Instant::now(),
}
}
}
impl Clock for SystemClock {
fn monotonic_millis(&self) -> u64 {
u64::try_from(self.started_at.elapsed().as_millis()).unwrap_or(u64::MAX)
}
fn epoch_seconds(&self) -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.ok()
.map_or(0, |duration| duration.as_secs())
}
}
#[derive(Debug, Clone)]
pub struct FixedClock {
epoch_seconds: u64,
monotonic_millis: u64,
}
impl FixedClock {
#[must_use]
pub const fn new(epoch_seconds: u64, monotonic_millis: u64) -> Self {
Self {
epoch_seconds,
monotonic_millis,
}
}
}
impl Clock for FixedClock {
fn monotonic_millis(&self) -> u64 {
self.monotonic_millis
}
fn epoch_seconds(&self) -> u64 {
self.epoch_seconds
}
}
#[must_use]
pub fn system_clock() -> SharedClock {
Arc::new(SystemClock::new())
}
#[cfg(test)]
mod tests {
use super::{Clock, FixedClock};
#[test]
fn fixed_clock_returns_injected_values() {
let clock = FixedClock::new(1_700_000_000, 42);
assert_eq!(clock.epoch_seconds(), 1_700_000_000);
assert_eq!(clock.monotonic_millis(), 42);
}
}