use std::fmt::{Debug, Formatter}; use std::ops::{Add, AddAssign, Sub, SubAssign}; use std::time::{Duration, Instant}; #[derive(Copy, Clone, PartialEq, PartialOrd)] pub struct Spot { /// Millisecond offset from the Unix Epoch - equivalent to Date.now() inner: f64, } impl Debug for Spot { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { self.inner.fmt(f) } } impl Spot { pub fn now() -> Self { Spot { inner: js_sys::Date::now(), } } /// Returns the amount of time elapsed since this instant was created. pub fn elapsed(&self) -> Duration { Spot::now() - *self } /// Returns the amount of time elapsed from another instant to this one, /// or zero duration if that instant is later than this one. pub fn duration_since(&self, earlier: Spot) -> Duration { match self.checked_duration_since(earlier) { Some(duration) => duration, None => std::process::abort(), } } /// Returns the amount of time elapsed from another instant to this one, /// or None if that instant is later than this one. /// /// Due to [monotonicity bugs], even under correct logical ordering of the passed `Instant`s, /// this method can return `None`. pub fn checked_duration_since(&self, earlier: Spot) -> Option<Duration> { if earlier.inner > self.inner { None } else { let millis = (self.inner - earlier.inner); Some(Duration::from_secs_f64(millis * 1000.0)) } } /// Returns the amount of time elapsed from another instant to this one, /// or zero duration if that instant is later than this one. pub fn saturating_duration_since(&self, earlier: Spot) -> Duration { match self.checked_duration_since(earlier) { Some(duration) => duration, None => std::process::abort(), } } /// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be represented as /// `Instant` (which means it's inside the bounds of the underlying data structure), `None` /// otherwise. pub fn checked_add(&self, duration: Duration) -> Option<Spot> { let duration_millis = duration.as_secs_f64() / 1000.0; Some(Spot { inner: self.inner + duration_millis, }) } /// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be represented as /// `Instant` (which means it's inside the bounds of the underlying data structure), `None` /// otherwise. pub fn checked_sub(&self, duration: Duration) -> Option<Spot> { let duration_millis = duration.as_secs_f64() / 1000.0; if duration_millis > self.inner { None } else { Some(Spot { inner: self.inner - duration_millis, }) } } /// Converts this `Spot` into a duration that represents the time that has elapsed between the /// UNIX Epoch and this `Spot` pub fn duration_since_epoch(&self) -> Duration { self.duration_since(Spot { inner: 0.0 }) } } impl Add<Duration> for Spot { type Output = Spot; /// # Panics /// /// This function may panic if the resulting point in time cannot be represented by the /// underlying data structure. See [`Spot::checked_add`] for a version without panic. fn add(self, other: Duration) -> Spot { match self.checked_add(other) { Some(duration) => duration, None => { eprint!("overflow when adding duration to instant"); std::process::abort(); } } } } impl AddAssign<Duration> for Spot { fn add_assign(&mut self, other: Duration) { *self = *self + other; } } impl Sub<Duration> for Spot { type Output = Spot; fn sub(self, other: Duration) -> Spot { match self.checked_sub(other) { Some(duration) => duration, None => { eprintln!("overflow when subtracting duration from instant"); std::process::abort(); } } } } impl SubAssign<Duration> for Spot { fn sub_assign(&mut self, other: Duration) { *self = *self - other; } } impl Sub<Spot> for Spot { type Output = Duration; /// Returns the amount of time elapsed from another instant to this one, /// or zero duration if that instant is later than this one. fn sub(self, other: Spot) -> Duration { self.duration_since(other) } }