Skip to content
Snippets Groups Projects
instant_web.rs 3.92 KiB
Newer Older
Louis's avatar
Louis committed
use std::fmt::{Debug, Formatter};
use std::ops::{Add, AddAssign, Sub, SubAssign};
use std::time::{Duration, Instant};
Louis's avatar
Louis committed

Louis's avatar
Louis committed
#[derive(Copy, Clone, PartialEq, PartialOrd)]
Louis's avatar
Louis committed
pub struct Spot {
	/// Millisecond offset from the Unix Epoch - equivalent to Date.now()
	inner: f64,
}

Louis's avatar
Louis committed
impl Debug for Spot {
	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
		self.inner.fmt(f)
	}
}

Louis's avatar
Louis committed
impl Spot {
	pub fn now() -> Self {
		Spot {
			inner: js_sys::Date::now(),
		}
	}
Louis's avatar
Louis committed
	/// Returns the amount of time elapsed since this instant was created.
	pub fn elapsed(&self) -> Duration {
		Spot::now() - *self
Louis's avatar
Louis committed
	}
Louis's avatar
Louis committed
	/// 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(),
		}
Louis's avatar
Louis committed
	}
Louis's avatar
Louis committed
	/// 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(),
		}
Louis's avatar
Louis committed
	}
	/// 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,
		})
Louis's avatar
Louis committed
	}
	/// 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 })
	}
Louis's avatar
Louis committed
}

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();
			}
		}
Louis's avatar
Louis committed
	}
}

impl AddAssign<Duration> for Spot {
	fn add_assign(&mut self, other: Duration) {
		*self = *self + other;
Louis's avatar
Louis committed
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();
			}
		}
Louis's avatar
Louis committed
	}
}

impl SubAssign<Duration> for Spot {
	fn sub_assign(&mut self, other: Duration) {
		*self = *self - other;
	}
}

Louis's avatar
Louis committed
impl Sub<Spot> for Spot {
	type Output = Duration;

Louis's avatar
Louis committed
	/// 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)