use std::cell::Cell;
use std::time::{Duration, Instant};
use super::EventTime;
pub trait InputClock {
fn now(&self) -> EventTime;
fn epoch(&self) -> Option<Instant> {
None
}
fn advance(&self, d: Duration) {
let _ = d;
}
}
#[derive(Debug, Clone)]
pub struct MonotonicClock {
epoch: Instant,
}
impl MonotonicClock {
pub fn new(epoch: Instant) -> Self {
Self { epoch }
}
pub fn epoch_instant(&self) -> Instant {
self.epoch
}
}
impl InputClock for MonotonicClock {
fn now(&self) -> EventTime {
EventTime::from_duration(Instant::now().saturating_duration_since(self.epoch))
}
fn epoch(&self) -> Option<Instant> {
Some(self.epoch)
}
}
#[derive(Debug, Clone)]
pub struct ManualClock(Cell<EventTime>);
impl ManualClock {
pub fn new(start: EventTime) -> Self {
Self(Cell::new(start))
}
pub fn set(&self, time: EventTime) {
self.0.set(time);
}
pub fn advance(&self, d: Duration) {
let next = self
.0
.get()
.checked_add(d)
.unwrap_or(EventTime::from_duration(Duration::MAX));
self.0.set(next);
}
}
impl Default for ManualClock {
fn default() -> Self {
Self::new(EventTime::ZERO)
}
}
impl InputClock for ManualClock {
fn now(&self) -> EventTime {
self.0.get()
}
fn advance(&self, d: Duration) {
ManualClock::advance(self, d);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_manual_clock_only_moves_when_told_to() {
let clock = ManualClock::new(EventTime::ZERO);
assert_eq!(clock.now(), EventTime::ZERO);
assert_eq!(clock.now(), EventTime::ZERO);
clock.advance(Duration::from_millis(500));
assert_eq!(clock.now(), EventTime::from_millis(500));
clock.advance(Duration::from_millis(250));
assert_eq!(clock.now(), EventTime::from_millis(750));
clock.set(EventTime::from_millis(10));
assert_eq!(clock.now(), EventTime::from_millis(10));
}
#[test]
fn a_manual_clock_saturates_rather_than_overflowing() {
let clock = ManualClock::new(EventTime::from_millis(1));
clock.advance(Duration::MAX);
clock.advance(Duration::MAX);
assert_eq!(clock.now().as_duration(), Duration::MAX);
}
#[test]
fn a_manual_clock_has_no_wall_clock_epoch() {
assert_eq!(ManualClock::default().epoch(), None);
}
#[test]
fn a_monotonic_clock_measures_from_its_epoch() {
let epoch = Instant::now();
let clock = MonotonicClock::new(epoch);
assert_eq!(clock.epoch(), Some(epoch));
assert_eq!(clock.epoch_instant(), epoch);
let a = clock.now();
let b = clock.now();
assert!(b >= a);
}
#[test]
fn a_monotonic_clock_clamps_a_future_epoch() {
let clock = MonotonicClock::new(Instant::now() + Duration::from_secs(3600));
assert_eq!(clock.now(), EventTime::ZERO);
}
#[test]
fn clocks_are_usable_through_the_trait_object() {
let clocks: Vec<std::rc::Rc<dyn InputClock>> = vec![
std::rc::Rc::new(ManualClock::new(EventTime::from_millis(4))),
std::rc::Rc::new(MonotonicClock::new(Instant::now())),
];
assert_eq!(clocks[0].now(), EventTime::from_millis(4));
assert!(clocks[1].epoch().is_some());
}
}