use std::time::Duration;
use crate::Clock;
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Instant {
inner: std::time::Instant,
}
impl Instant {
#[must_use]
#[inline]
pub fn elapsed(&self, clock: &mut Clock) -> Duration {
clock.now().saturating_duration_since(*self)
}
#[must_use]
#[inline]
pub fn saturating_duration_since(&self, earlier: Self) -> Duration {
self.inner.saturating_duration_since(earlier.inner)
}
#[must_use]
pub fn duration_since(&self, earlier: Self) -> Duration {
self.saturating_duration_since(earlier)
}
#[must_use]
pub fn checked_duration_since(&self, earlier: Self) -> Option<Duration> {
self.inner.checked_duration_since(earlier.inner)
}
#[must_use]
pub fn checked_add(&self, duration: Duration) -> Option<Self> {
self.inner.checked_add(duration).map(Self::from)
}
#[must_use]
pub fn checked_sub(&self, duration: Duration) -> Option<Self> {
self.inner.checked_sub(duration).map(Self::from)
}
}
impl From<std::time::Instant> for Instant {
#[inline]
fn from(inner: std::time::Instant) -> Self {
Self { inner }
}
}
impl From<Instant> for std::time::Instant {
#[inline]
fn from(instant: Instant) -> Self {
instant.inner
}
}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use std::panic::{RefUnwindSafe, UnwindSafe};
use mockall::Sequence;
use super::*;
use crate::pal::{MockPlatform, MockTimeSource};
static_assertions::assert_impl_all!(Instant: UnwindSafe, RefUnwindSafe);
#[test]
fn saturating_duration_since_can_math() {
use std::time::Duration;
let base_instant = std::time::Instant::now();
let instant1 = Instant::from(base_instant);
let instant2 = Instant::from(base_instant + Duration::from_secs(10));
let instant3 = Instant::from(base_instant + Duration::from_secs(50));
let duration_1_to_2 = instant2.saturating_duration_since(instant1);
assert_eq!(duration_1_to_2, Duration::from_secs(10));
let duration_1_to_3 = instant3.saturating_duration_since(instant1);
assert_eq!(duration_1_to_3, Duration::from_secs(50));
let duration_2_to_3 = instant3.saturating_duration_since(instant2);
assert_eq!(duration_2_to_3, Duration::from_secs(40));
let duration_reverse_1 = instant1.saturating_duration_since(instant2);
assert_eq!(duration_reverse_1, Duration::ZERO);
let duration_reverse_2 = instant1.saturating_duration_since(instant3);
assert_eq!(duration_reverse_2, Duration::ZERO);
let duration_reverse_3 = instant2.saturating_duration_since(instant3);
assert_eq!(duration_reverse_3, Duration::ZERO);
let duration_same = instant1.saturating_duration_since(instant1);
assert_eq!(duration_same, Duration::ZERO);
}
#[test]
fn duration_since_can_math() {
let a = std::time::Instant::now();
let b = a.checked_add(Duration::from_millis(100)).unwrap();
let mut time_source = MockTimeSource::new();
let mut seq = Sequence::new();
time_source
.expect_now()
.once()
.in_sequence(&mut seq)
.return_once(move || a);
time_source
.expect_now()
.once()
.in_sequence(&mut seq)
.return_once(move || b);
let mut platform = MockPlatform::new();
platform
.expect_new_time_source()
.once()
.return_once(move || time_source);
let mut clock = Clock::from_pal(platform.into());
let instant1 = clock.now();
let instant2 = clock.now();
let duration = instant1.saturating_duration_since(instant2);
assert_eq!(duration, Duration::ZERO);
let duration = instant2.saturating_duration_since(instant1);
assert_eq!(duration, Duration::from_millis(100));
}
#[test]
fn elapsed_can_math() {
let a = std::time::Instant::now();
let b = a.checked_add(Duration::from_millis(100)).unwrap();
let mut time_source = MockTimeSource::new();
let mut seq = Sequence::new();
time_source
.expect_now()
.once()
.in_sequence(&mut seq)
.return_once(move || a);
time_source
.expect_now()
.once()
.in_sequence(&mut seq)
.return_once(move || b);
let mut platform = MockPlatform::new();
platform
.expect_new_time_source()
.once()
.return_once(move || time_source);
let mut clock = Clock::from_pal(platform.into());
let instant1 = clock.now();
let duration = instant1.elapsed(&mut clock);
assert_eq!(duration, Duration::from_millis(100));
}
#[test]
fn checked_duration_since_can_math() {
use std::time::Duration;
let base_instant = std::time::Instant::now();
let instant1 = Instant::from(base_instant);
let instant2 = Instant::from(base_instant + Duration::from_secs(10));
let duration = instant2.checked_duration_since(instant1);
assert_eq!(duration, Some(Duration::from_secs(10)));
let duration_reverse = instant1.checked_duration_since(instant2);
assert_eq!(duration_reverse, None);
let duration_same = instant1.checked_duration_since(instant1);
assert_eq!(duration_same, Some(Duration::ZERO));
}
#[test]
fn duration_since_is_alias_for_saturating_duration_since() {
use std::time::Duration;
let base_instant = std::time::Instant::now();
let instant1 = Instant::from(base_instant);
let instant2 = Instant::from(base_instant + Duration::from_secs(5));
let duration_saturating = instant2.saturating_duration_since(instant1);
let duration_alias = instant2.duration_since(instant1);
assert_eq!(duration_saturating, duration_alias);
let duration_saturating_reverse = instant1.saturating_duration_since(instant2);
let duration_alias_reverse = instant1.duration_since(instant2);
assert_eq!(duration_saturating_reverse, duration_alias_reverse);
assert_eq!(duration_alias_reverse, Duration::ZERO);
}
#[test]
fn checked_add_can_math() {
use std::time::Duration;
let base_instant = std::time::Instant::now();
let instant = Instant::from(base_instant);
let duration = Duration::from_secs(5);
let later_instant = instant.checked_add(duration).unwrap();
let expected = Instant::from(base_instant + duration);
assert_eq!(later_instant, expected);
assert!(later_instant > instant);
let same_instant = instant.checked_add(Duration::ZERO).unwrap();
assert_eq!(same_instant, instant);
}
#[test]
fn checked_sub_can_math() {
use std::time::Duration;
let base_instant = std::time::Instant::now();
let duration = Duration::from_secs(10);
let later_instant = base_instant + duration;
let instant = Instant::from(later_instant);
let earlier_instant = instant.checked_sub(Duration::from_secs(5)).unwrap();
let expected = Instant::from(later_instant.checked_sub(Duration::from_secs(5)).unwrap());
assert_eq!(earlier_instant, expected);
assert!(earlier_instant < instant);
let same_instant = instant.checked_sub(Duration::ZERO).unwrap();
assert_eq!(same_instant, instant);
let base_again = instant.checked_sub(duration).unwrap();
let expected_base = Instant::from(base_instant);
assert_eq!(base_again, expected_base);
}
}