use std::fmt;
use std::ops;
use std::time;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MonotonicTs(u64);
impl MonotonicTs {
pub const ZERO: Self = Self(0);
#[must_use]
pub fn from_nanos(nanos: u64) -> Self {
Self(nanos)
}
#[must_use]
pub fn as_nanos(self) -> u64 {
self.0
}
#[must_use]
pub fn as_secs_f64(self) -> f64 {
self.0 as f64 / 1_000_000_000.0
}
#[must_use]
pub fn checked_duration_since(self, other: Self) -> Option<Duration> {
self.0.checked_sub(other.0).map(Duration::from_nanos)
}
#[must_use]
pub fn saturating_duration_since(self, other: Self) -> Duration {
Duration::from_nanos(self.0.saturating_sub(other.0))
}
}
impl fmt::Display for MonotonicTs {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}ns", self.0)
}
}
impl fmt::Debug for MonotonicTs {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "MonotonicTs({}ns)", self.0)
}
}
impl ops::Add<Duration> for MonotonicTs {
type Output = Self;
fn add(self, rhs: Duration) -> Self {
Self(self.0 + rhs.as_nanos())
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct WallTs(i64);
impl WallTs {
#[must_use]
pub fn from_micros(micros: i64) -> Self {
Self(micros)
}
#[must_use]
pub fn now() -> Self {
let d = time::SystemTime::now()
.duration_since(time::UNIX_EPOCH)
.unwrap_or_default();
Self(d.as_micros() as i64)
}
#[must_use]
pub fn as_micros(self) -> i64 {
self.0
}
}
impl fmt::Display for WallTs {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}μs", self.0)
}
}
impl fmt::Debug for WallTs {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "WallTs({}μs)", self.0)
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Duration(u64);
impl Duration {
pub const ZERO: Self = Self(0);
#[must_use]
pub fn from_nanos(nanos: u64) -> Self {
Self(nanos)
}
#[must_use]
pub fn from_micros(micros: u64) -> Self {
Self(micros * 1_000)
}
#[must_use]
pub fn from_millis(millis: u64) -> Self {
Self(millis * 1_000_000)
}
#[must_use]
pub fn from_secs(secs: u64) -> Self {
Self(secs * 1_000_000_000)
}
#[must_use]
pub fn as_nanos(self) -> u64 {
self.0
}
#[must_use]
pub fn as_secs_f64(self) -> f64 {
self.0 as f64 / 1_000_000_000.0
}
}
impl fmt::Display for Duration {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.0 >= 1_000_000_000 {
write!(f, "{:.3}s", self.as_secs_f64())
} else if self.0 >= 1_000_000 {
write!(f, "{:.3}ms", self.0 as f64 / 1_000_000.0)
} else if self.0 >= 1_000 {
write!(f, "{:.1}μs", self.0 as f64 / 1_000.0)
} else {
write!(f, "{}ns", self.0)
}
}
}
impl fmt::Debug for Duration {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Duration({}ns)", self.0)
}
}
impl From<std::time::Duration> for Duration {
fn from(d: std::time::Duration) -> Self {
Self(d.as_nanos() as u64)
}
}
impl From<Duration> for std::time::Duration {
fn from(d: Duration) -> Self {
std::time::Duration::from_nanos(d.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn monotonic_ordering() {
let a = MonotonicTs::from_nanos(100);
let b = MonotonicTs::from_nanos(200);
assert!(a < b);
}
#[test]
fn duration_conversion() {
let d = Duration::from_millis(42);
let std_d: std::time::Duration = d.into();
assert_eq!(std_d.as_millis(), 42);
}
#[test]
fn checked_duration_since() {
let a = MonotonicTs::from_nanos(300);
let b = MonotonicTs::from_nanos(100);
let d = a.checked_duration_since(b).unwrap();
assert_eq!(d.as_nanos(), 200);
assert!(b.checked_duration_since(a).is_none());
}
}