#![cfg_attr(not(feature = "std"), no_std)]
#![forbid(unsafe_code)]
#![warn(missing_docs)]
use core::cell::Cell;
pub trait Clock {
fn now(&self) -> u64;
}
impl<C: Clock + ?Sized> Clock for &C {
fn now(&self) -> u64 {
(**self).now()
}
}
#[derive(Debug, Default)]
pub struct ManualClock {
tick: Cell<u64>,
}
impl ManualClock {
pub const fn new(start: u64) -> Self {
Self {
tick: Cell::new(start),
}
}
pub fn set(&self, tick: u64) {
self.tick.set(tick);
}
pub fn advance(&self, delta: u64) {
self.tick.set(self.tick.get().saturating_add(delta));
}
}
impl Clock for ManualClock {
fn now(&self) -> u64 {
self.tick.get()
}
}
#[cfg(feature = "std")]
#[derive(Debug, Clone)]
pub struct MonotonicClock {
origin: std::time::Instant,
}
#[cfg(feature = "std")]
impl MonotonicClock {
pub fn new() -> Self {
Self {
origin: std::time::Instant::now(),
}
}
}
#[cfg(feature = "std")]
impl Default for MonotonicClock {
fn default() -> Self {
Self::new()
}
}
#[cfg(feature = "std")]
impl Clock for MonotonicClock {
fn now(&self) -> u64 {
let millis = self.origin.elapsed().as_millis();
if millis > u64::MAX as u128 {
u64::MAX
} else {
millis as u64
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn manual_clock_reads_sets_and_advances() {
let clock = ManualClock::new(10);
assert_eq!(clock.now(), 10);
clock.advance(5);
assert_eq!(clock.now(), 15);
clock.set(100);
assert_eq!(clock.now(), 100);
}
#[test]
fn manual_clock_advance_saturates() {
let clock = ManualClock::new(u64::MAX - 1);
clock.advance(10);
assert_eq!(clock.now(), u64::MAX);
}
#[test]
fn manual_clock_default_is_zero() {
assert_eq!(ManualClock::default().now(), 0);
}
#[test]
fn clock_is_object_safe_and_reference_forwards() {
let clock = ManualClock::new(7);
let by_ref: &dyn Clock = &clock;
assert_eq!(by_ref.now(), 7);
fn read(c: impl Clock) -> u64 {
c.now()
}
assert_eq!(read(&clock), 7);
}
#[cfg(feature = "std")]
#[test]
fn monotonic_clock_is_non_decreasing() {
let clock = MonotonicClock::new();
let a = clock.now();
let b = clock.now();
assert!(b >= a);
assert!(MonotonicClock::default().now() < u64::MAX);
}
}