use core::ops::Sub;
use core::time::Duration;
pub trait Time: Copy + Sub<Duration, Output = Self> where Self: Sized {
fn now() -> Self;
fn elapsed(&self) -> Duration;
fn duration_since(&self, earlier: Self) -> Duration;
fn duration_since_epoch() -> Duration;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Eternity;
impl Time for Eternity {
fn now() -> Self {
Self
}
fn duration_since(&self, _earlier: Self) -> Duration {
Duration::from_secs(0)
}
fn duration_since_epoch() -> Duration {
Duration::from_secs(0)
}
fn elapsed(&self) -> Duration {
Duration::from_secs(0)
}
}
impl Sub<Duration> for Eternity {
type Output = Self;
fn sub(self, _other: Duration) -> Self {
self
}
}
#[cfg(not(feature = "no-std"))]
impl Time for std::time::Instant {
fn now() -> Self {
std::time::Instant::now()
}
fn duration_since(&self, earlier: Self) -> Duration {
let now = Self::now();
if now > earlier { now - earlier } else { Duration::from_secs(0) }
}
fn duration_since_epoch() -> Duration {
use std::time::SystemTime;
SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap()
}
fn elapsed(&self) -> Duration {
std::time::Instant::elapsed(self)
}
}
#[cfg(test)]
pub mod tests {
use super::{Time, Eternity};
use core::time::Duration;
use core::ops::Sub;
use core::cell::Cell;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SinceEpoch(Duration);
impl SinceEpoch {
thread_local! {
static ELAPSED: Cell<Duration> = core::cell::Cell::new(Duration::from_secs(0));
}
pub fn advance(duration: Duration) {
Self::ELAPSED.with(|elapsed| elapsed.set(elapsed.get() + duration))
}
}
impl Time for SinceEpoch {
fn now() -> Self {
Self(Self::duration_since_epoch())
}
fn duration_since(&self, earlier: Self) -> Duration {
self.0 - earlier.0
}
fn duration_since_epoch() -> Duration {
Self::ELAPSED.with(|elapsed| elapsed.get())
}
fn elapsed(&self) -> Duration {
Self::duration_since_epoch() - self.0
}
}
impl Sub<Duration> for SinceEpoch {
type Output = Self;
fn sub(self, other: Duration) -> Self {
Self(self.0 - other)
}
}
#[test]
fn time_passes_when_advanced() {
let now = SinceEpoch::now();
assert_eq!(now.elapsed(), Duration::from_secs(0));
SinceEpoch::advance(Duration::from_secs(1));
SinceEpoch::advance(Duration::from_secs(1));
let elapsed = now.elapsed();
let later = SinceEpoch::now();
assert_eq!(elapsed, Duration::from_secs(2));
assert_eq!(later - elapsed, now);
}
#[test]
fn time_never_passes_in_an_eternity() {
let now = Eternity::now();
let elapsed = now.elapsed();
let later = Eternity::now();
assert_eq!(now.elapsed(), Duration::from_secs(0));
assert_eq!(later - elapsed, now);
}
}