use std::time::{SystemTime, UNIX_EPOCH};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Source {
System,
Fixed,
}
#[derive(Debug, Clone, Copy)]
pub struct Clock {
now_ms: u64,
source: Source,
}
impl Clock {
pub fn system() -> Clock {
Clock {
now_ms: Clock::fine_now_ms(),
source: Source::System,
}
}
pub const fn fixed(ms: u64) -> Clock {
Clock {
now_ms: ms,
source: Source::Fixed,
}
}
#[inline]
pub const fn now_ms(&self) -> u64 {
self.now_ms
}
#[inline]
pub fn refresh(&mut self) {
if self.source == Source::System {
self.now_ms = Clock::fine_now_ms();
}
}
#[inline]
pub const fn set(&mut self, ms: u64) {
self.now_ms = ms;
}
#[inline]
pub const fn advance(&mut self, ms: u64) {
self.now_ms = self.now_ms.saturating_add(ms);
}
#[inline]
pub fn fine_now_ms() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map_or(0, |d| d.as_millis() as u64)
}
}
impl Default for Clock {
fn default() -> Clock {
Clock::system()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_fixed_clock_stays_where_it_is_put() {
let mut c = Clock::fixed(1_000);
assert_eq!(c.now_ms(), 1_000);
c.refresh();
assert_eq!(c.now_ms(), 1_000, "refresh moved a fixed clock");
c.advance(500);
assert_eq!(c.now_ms(), 1_500);
c.set(7);
assert_eq!(c.now_ms(), 7);
}
#[test]
fn a_system_clock_reads_a_plausible_time() {
let c = Clock::system();
assert!(c.now_ms() > 1_577_836_800_000, "clock read {}", c.now_ms());
}
#[test]
fn a_system_clock_does_not_move_until_it_is_refreshed() {
let mut c = Clock::system();
let first = c.now_ms();
let mut spin = 0u64;
for i in 0..200_000u64 {
spin = spin.wrapping_add(i);
}
assert_eq!(c.now_ms(), first, "the clock moved on its own {spin}");
c.refresh();
assert!(c.now_ms() >= first);
}
#[test]
fn advancing_past_the_end_of_time_stops_there() {
let mut c = Clock::fixed(u64::MAX - 1);
c.advance(10);
assert_eq!(c.now_ms(), u64::MAX);
}
}