pub use std::time::Duration;
use futures::channel::oneshot;
use futures::future::{select, Either, Future};
use futures::pin_mut;
use gloo_timers::future::TimeoutFuture;
use ordered_float::OrderedFloat;
use std::ops::{Add, AddAssign, Sub, SubAssign};
use thiserror::Error;
pub async fn sleep(duration: Duration) {
let (tx, rx) = oneshot::channel();
crate::task::spawn_local(async move {
TimeoutFuture::new(duration.as_millis() as u32).await;
tx.send(()).ok();
});
rx.await.ok();
}
pub async fn timeout<T>(duration: Option<Duration>, fut: T) -> Result<T::Output, Elapsed>
where
T: Future,
{
if let Some(duration) = duration {
let timeout = sleep(duration);
pin_mut!(timeout);
pin_mut!(fut);
let res = select(timeout, fut).await;
match res {
Either::Left(((), _unfinished_fut)) => Err(Elapsed),
Either::Right((output, _timeout_fut)) => Ok(output),
}
} else {
Ok(fut.await)
}
}
pub struct Elapsed;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Instant {
inner: OrderedFloat<f64>,
}
impl Instant {
const fn new(value: f64) -> Instant {
Self {
inner: OrderedFloat(value),
}
}
pub fn now() -> Instant {
let value = web_sys::window()
.expect("not in a browser")
.performance()
.expect("performance object not available")
.now();
Instant::new(value)
}
pub fn duration_since(&self, earlier: Instant) -> Duration {
*self - earlier
}
pub fn elapsed(&self) -> Duration {
Instant::now() - *self
}
}
impl Add<Duration> for Instant {
type Output = Instant;
fn add(self, other: Duration) -> Instant {
let inner = self.inner + OrderedFloat(other.as_millis() as f64);
Instant { inner }
}
}
impl Sub<Duration> for Instant {
type Output = Instant;
fn sub(self, other: Duration) -> Instant {
let inner = self.inner - OrderedFloat(other.as_millis() as f64);
Instant { inner }
}
}
impl Sub<Instant> for Instant {
type Output = Duration;
fn sub(self, other: Instant) -> Duration {
let ms = self.inner - other.inner;
assert!(ms >= OrderedFloat(0.0));
Duration::from_millis(ms.into_inner() as u64)
}
}
#[derive(Debug, Error)]
#[error("system time error")]
pub struct SystemTimeError;
pub const UNIX_EPOCH: SystemTime = SystemTime::new(0.0);
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct SystemTime {
inner: OrderedFloat<f64>,
}
impl SystemTime {
const fn new(value: f64) -> Self {
Self {
inner: OrderedFloat(value),
}
}
pub fn now() -> SystemTime {
let val = js_sys::Date::now();
SystemTime::new(val)
}
pub fn duration_since(&self, earlier: SystemTime) -> Result<Duration, SystemTimeError> {
let dur_ms = self.inner - earlier.inner;
if dur_ms < OrderedFloat(0.0) {
Err(SystemTimeError)
} else {
Ok(Duration::from_millis(dur_ms.into_inner() as u64))
}
}
pub fn elapsed(&self) -> Result<Duration, SystemTimeError> {
self.duration_since(SystemTime::now())
}
pub fn checked_add(&self, duration: Duration) -> Option<SystemTime> {
Some(*self + duration)
}
pub fn checked_sub(&self, duration: Duration) -> Option<SystemTime> {
Some(*self - duration)
}
}
impl Add<Duration> for SystemTime {
type Output = SystemTime;
fn add(self, other: Duration) -> SystemTime {
let inner = self.inner + OrderedFloat(other.as_millis() as f64);
SystemTime { inner }
}
}
impl Sub<Duration> for SystemTime {
type Output = SystemTime;
fn sub(self, other: Duration) -> SystemTime {
let inner = self.inner - OrderedFloat(other.as_millis() as f64);
SystemTime { inner }
}
}
impl AddAssign<Duration> for SystemTime {
fn add_assign(&mut self, rhs: Duration) {
*self = *self + rhs;
}
}
impl SubAssign<Duration> for SystemTime {
fn sub_assign(&mut self, rhs: Duration) {
*self = *self - rhs;
}
}