From bd3445c23d771cf95e57a00cc7936a6212324079 Mon Sep 17 00:00:00 2001 From: Louis Capitanchik <contact@louiscap.co> Date: Wed, 4 Jan 2023 15:17:40 +0000 Subject: [PATCH] Implement full Instant interface --- .gitignore | 1 + Cargo.toml | 2 +- src/instant_desktop.rs | 85 ++++++++++++++++++++++++++++++---- src/instant_web.rs | 102 +++++++++++++++++++++++++++++++++++------ src/lib.rs | 4 +- 5 files changed, 167 insertions(+), 27 deletions(-) diff --git a/.gitignore b/.gitignore index 4fffb2f..3d84f13 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target /Cargo.lock +.idea/ \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 26ac5a7..9e2d4d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "web_instant" -version = "0.1.0" +version = "0.2.0" edition = "2021" description = "Cross platform impl of Instant" authors = [ diff --git a/src/instant_desktop.rs b/src/instant_desktop.rs index 8bbd5e0..73cf8b3 100644 --- a/src/instant_desktop.rs +++ b/src/instant_desktop.rs @@ -1,33 +1,98 @@ -use std::ops::Sub; +use std::fmt::{Debug, Formatter}; +use std::ops::{Add, AddAssign, Sub, SubAssign}; use std::time::{Duration, Instant}; -#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)] +#[derive(Copy, Clone, PartialEq, PartialOrd)] pub struct Spot { inner: Instant, } +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: Instant::now(), } } - pub fn as_secs(&self) -> u64 { - self.as_duration().as_secs() + /// 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 { + self.checked_duration_since(earlier).unwrap_or_default() + } + /// 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> { + self.inner.checked_duration_since(earlier.inner) } - pub fn as_duration(&self) -> Duration { - self.inner.elapsed() + /// 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 { + self.inner.saturating_duration_since(earlier.inner).unwrap_or_default() } + /// 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> { + self.inner.checked_add(duration).map(|inner| Spot { inner }) + } + /// 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> { + self.inner.checked_sub(duration).map(|inner| Spot { inner }) + } +} - pub fn elapsed(&self) -> Duration { - self.as_duration() +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 { + self.checked_add(other).expect("overflow when adding duration to instant") + } +} + +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 { + self.checked_sub(other).expect("overflow when subtracting duration from instant") + } +} + +impl SubAssign<Duration> for Spot { + fn sub_assign(&mut self, other: Duration) { + *self = *self - other; } } impl Sub<Spot> for Spot { type Output = Duration; - fn sub(self, rhs: Spot) -> Self::Output { - self.inner - rhs.inner + /// 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) } } diff --git a/src/instant_web.rs b/src/instant_web.rs index 36e892a..9a6439d 100644 --- a/src/instant_web.rs +++ b/src/instant_web.rs @@ -1,37 +1,111 @@ -use std::ops::Sub; +use std::fmt::{Debug, Formatter}; +use std::ops::{Add, AddAssign, Sub, SubAssign}; use std::time::Duration; -#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)] +#[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(), } } - pub fn as_secs(&self) -> u64 { - self.as_duration().as_secs() + /// Returns the amount of time elapsed since this instant was created. + pub fn elapsed(&self) -> Duration { + Spot::now() - *self } - pub fn as_duration(&self) -> Duration { - Self::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 { + self.checked_duration_since(earlier).unwrap_or_default() } - pub fn elapsed(&self) -> Duration { - self.as_duration() + /// 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 { + self.checked_duration_since(earlier).unwrap_or_default() + } + /// 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 }) + } + } +} + +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 { + self.checked_add(other).expect("overflow when adding duration to instant") + } +} + +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 { + self.checked_sub(other).expect("overflow when subtracting duration from instant") + } +} + +impl SubAssign<Duration> for Spot { + fn sub_assign(&mut self, other: Duration) { + *self = *self - other; + } +} + + impl Sub<Spot> for Spot { type Output = Duration; - fn sub(self, rhs: Spot) -> Self::Output { - let diff = (self.inner - rhs.inner).max(0.0); - let secs = (diff as u64) / 1_000; - let nanos = (((diff as u64) % 1_000) as u32) * 1_000_000; - - Duration::new(secs, nanos) + /// 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) } } diff --git a/src/lib.rs b/src/lib.rs index cda40fc..409aa99 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ -#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(target_family = "wasm"))] mod instant_desktop; -#[cfg(target_arch = "wasm32")] +// #[cfg(target_family = "wasm")] mod instant_web; #[cfg(not(target_arch = "wasm32"))] -- GitLab