#![deny(missing_docs)]
pub mod clocks;
pub mod prelude;
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
pub use crate::clocks::{Clock, DefaultClock};
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Instant(i64);
impl Instant {
pub const EPOCH: Self = Self(0);
pub const FOREVER_AGO: Self = Self(i64::min_value());
pub const SOMEDAY: Self = Self(i64::max_value());
}
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Duration(i64);
impl Duration {
pub const NANOSECOND: Self = Self(1);
pub const MICROSECOND: Self = Self(1000 * Self::NANOSECOND.0);
pub const MILLISECOND: Self = Self(1000 * Self::MICROSECOND.0);
pub const SECOND: Self = Self(1000 * Self::MILLISECOND.0);
pub const MINUTE: Self = Self(60 * Self::SECOND.0);
pub const HOUR: Self = Self(60 * Self::MINUTE.0);
pub const FOREVER: Self = Self(i64::max_value());
pub const fn from_nanos(nanos: i64) -> Self {
Self(nanos)
}
pub const fn as_nanos(self) -> i64 {
self.0
}
}
impl Add<Duration> for Instant {
type Output = Instant;
fn add(self, rhs: Duration) -> Instant {
Instant(self.0 + rhs.0)
}
}
impl Add<Duration> for Duration {
type Output = Duration;
fn add(self, rhs: Duration) -> Duration {
Duration(self.0 + rhs.0)
}
}
impl Div<i64> for Duration {
type Output = Duration;
fn div(self, rhs: i64) -> Duration {
Duration(self.0 / rhs)
}
}
impl Mul<Duration> for i64 {
type Output = Duration;
fn mul(self, rhs: Duration) -> Duration {
Duration(self * rhs.0)
}
}
impl Mul<i64> for Duration {
type Output = Duration;
fn mul(self, rhs: i64) -> Duration {
Duration(self.0 * rhs)
}
}
impl Neg for Duration {
type Output = Duration;
fn neg(self) -> Duration {
Duration(-self.0)
}
}
impl Sub<Duration> for Instant {
type Output = Instant;
fn sub(self, rhs: Duration) -> Instant {
Instant(self.0 - rhs.0)
}
}
impl Sub<Duration> for Duration {
type Output = Duration;
fn sub(self, rhs: Duration) -> Duration {
Duration(self.0 - rhs.0)
}
}
impl Sub<Instant> for Instant {
type Output = Duration;
fn sub(self, rhs: Instant) -> Duration {
Duration(self.0 - rhs.0)
}
}
impl AddAssign<Duration> for Instant {
fn add_assign(&mut self, other: Duration) {
self.0 += other.0;
}
}
impl AddAssign<Duration> for Duration {
fn add_assign(&mut self, other: Duration) {
self.0 += other.0;
}
}
impl DivAssign<i64> for Duration {
fn div_assign(&mut self, other: i64) {
self.0 /= other
}
}
impl MulAssign<i64> for Duration {
fn mul_assign(&mut self, other: i64) {
self.0 *= other;
}
}
impl SubAssign<Duration> for Duration {
fn sub_assign(&mut self, other: Duration) {
self.0 -= other.0;
}
}
impl SubAssign<Duration> for Instant {
fn sub_assign(&mut self, other: Duration) {
self.0 -= other.0;
}
}
#[cfg(test)]
mod tests {
use super::*;
use quickcheck::{Arbitrary, Gen};
use quickcheck_macros::quickcheck;
use std::convert::TryInto;
const MINUS_FOREVER: Duration = Duration(i64::min_value());
#[test]
fn special_instants() {
assert!(Instant::FOREVER_AGO < Instant::EPOCH);
assert!(Instant::FOREVER_AGO < Instant::SOMEDAY);
assert!(Instant::EPOCH < Instant::SOMEDAY);
assert_eq!(Instant::SOMEDAY - Instant::EPOCH, Duration::FOREVER);
assert_eq!(Instant::FOREVER_AGO - Instant::EPOCH, MINUS_FOREVER);
}
#[test]
fn special_durations() {
assert_eq!(Duration::default(), Duration(0));
assert_eq!(Duration::NANOSECOND, Duration(1));
assert!(Duration::NANOSECOND > Duration(0));
assert_eq!(Duration::NANOSECOND, Duration::from_nanos(1));
assert_eq!(Duration::NANOSECOND.as_nanos(), 1);
assert_eq!(Duration::MICROSECOND, 1000 * Duration::NANOSECOND);
assert_eq!(Duration::MILLISECOND, 1000 * Duration::MICROSECOND);
assert_eq!(Duration::SECOND, 1000 * Duration::MILLISECOND);
assert_eq!(Duration::MINUTE, 60 * Duration::SECOND);
assert_eq!(Duration::HOUR, 60 * Duration::MINUTE);
assert!(Duration::HOUR < Duration::FOREVER);
}
impl Arbitrary for Duration {
fn arbitrary<G: Gen>(g: &mut G) -> Self {
Duration(<i64 as Arbitrary>::arbitrary::<G>(g))
}
}
impl Arbitrary for Instant {
fn arbitrary<G: Gen>(g: &mut G) -> Self {
Instant(<i64 as Arbitrary>::arbitrary::<G>(g))
}
}
#[quickcheck]
fn duration_properties(d: Duration) -> bool {
let mut dt0 = d;
dt0 *= 0;
let mut dt1 = d;
dt1 *= 1;
let mut dd1 = d;
dd1 /= 1;
let always_check = d >= MINUS_FOREVER
&& d <= Duration::FOREVER
&& 0 * d == Duration::default()
&& d * 0 == 0 * d
&& dt0 == 0 * d
&& 1 * d == d
&& d * 1 == 1 * d
&& dt1 == 1 * d
&& d / 1 == d
&& dd1 == d / 1
&& Duration::from_nanos(d.as_nanos()) == d;
if d == MINUS_FOREVER {
return always_check;
}
let mut dtm1 = d;
dtm1 *= -1;
let mut ddm1 = d;
ddm1 /= -1;
(-d != d || d.as_nanos() == 0)
&& (-1) * d == -d
&& d * (-1) == -d
&& d / (-1) == -d
&& dtm1 == -d
&& ddm1 == -d
&& -(-d) == d
}
#[quickcheck]
fn duration_duration_properties(d1: Duration, d2: Duration) -> bool {
let sum = (i128::from(d1.as_nanos()) + i128::from(d2.as_nanos()))
.try_into()
.map(Duration::from_nanos);
let sum_test = if let Ok(sum) = sum {
let mut d1p2 = d1;
d1p2 += d2;
d1 + d2 == sum && d1p2 == d1 + d2
} else {
true
};
let diff = (i128::from(d1.as_nanos()) - i128::from(d2.as_nanos()))
.try_into()
.map(Duration::from_nanos);
let diff_test = if let Ok(diff) = diff {
let mut d1m2 = d1;
d1m2 -= d2;
d1 - d2 == diff && d1m2 == d1 - d2
} else {
true
};
sum_test && diff_test
}
#[quickcheck]
fn duration_i64_properties(d: Duration, i: i64) -> bool {
let mul = (i128::from(d.as_nanos()) * i128::from(i))
.try_into()
.map(Duration::from_nanos);
let mul_test = if let Ok(mul) = mul {
let mut dti = d;
dti *= i;
d * i == mul && i * d == d * i && dti == d * i
} else {
true
};
if i == 0 {
return mul_test;
}
let div = (i128::from(d.as_nanos()) / i128::from(i))
.try_into()
.map(Duration::from_nanos);
let div_test = if let Ok(div) = div {
let mut doi = d;
doi /= i;
d / i == div && doi == d / i
} else {
true
};
mul_test && div_test
}
#[quickcheck]
fn instant_properties(i: Instant) -> bool {
i >= Instant::FOREVER_AGO && i <= Instant::SOMEDAY
}
#[quickcheck]
fn instant_duration_properties(i: Instant, d: Duration) -> bool {
let sum = (i128::from(i.0) + i128::from(d.as_nanos()))
.try_into()
.map(Instant);
let sum_test = if let Ok(sum) = sum {
let mut ipd = i;
ipd += d;
i + d == sum && ipd == i + d
} else {
true
};
let diff = (i128::from(i.0) - i128::from(d.as_nanos()))
.try_into()
.map(Instant);
let diff_test = if let Ok(diff) = diff {
let mut imd = i;
imd -= d;
i - d == diff && imd == i - d
} else {
true
};
sum_test && diff_test
}
#[quickcheck]
fn instant_instant_properties(i1: Instant, i2: Instant) -> bool {
let diff = (i128::from(i1.0) - i128::from(i2.0))
.try_into()
.map(Duration::from_nanos);
if let Ok(diff) = diff {
i1 - i2 == diff
} else {
true
}
}
}