easytime 0.1.2

Providing wrapper types for safely performing panic-free checked arithmetic on instants and durations.
Documentation
use core::{
    ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign},
    time,
};

use super::pair_and_then;

/// A `Duration` type to represent a span of time, typically used for system
/// timeouts.
///
/// Each `Duration` is composed of a whole number of seconds and a fractional part
/// represented in nanoseconds.  If the underlying system does not support
/// nanosecond-level precision, APIs binding a system timeout will typically round up
/// the number of nanoseconds.
///
/// `Duration`s implement many common traits, including [`Add`], [`Sub`], and other
/// [`ops`] traits.
///
/// [`Add`]: std::ops::Add
/// [`Sub`]: std::ops::Sub
/// [`ops`]: std::ops
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Duration(pub(crate) Option<time::Duration>);

impl Duration {
    /// Creates a new `Duration` from the specified number of whole seconds and
    /// additional nanoseconds.
    ///
    /// If the number of nanoseconds is greater than 1 billion (the number of
    /// nanoseconds in a second), then it will carry over into the seconds provided.
    #[inline]
    pub fn new(secs: u64, nanos: u32) -> Duration {
        let secs = time::Duration::from_secs(secs);
        let nanos = time::Duration::from_nanos(u64::from(nanos));
        Duration(secs.checked_add(nanos))
    }

    /// Creates a new `Duration` from the specified number of whole seconds.
    #[inline]
    pub const fn from_secs(secs: u64) -> Duration {
        Duration(Some(time::Duration::from_secs(secs)))
    }

    /// Creates a new `Duration` from the specified number of milliseconds.
    #[inline]
    pub const fn from_millis(millis: u64) -> Duration {
        Duration(Some(time::Duration::from_millis(millis)))
    }

    /// Creates a new `Duration` from the specified number of microseconds.
    #[inline]
    pub const fn from_micros(micros: u64) -> Duration {
        Duration(Some(time::Duration::from_micros(micros)))
    }

    /// Creates a new `Duration` from the specified number of nanoseconds.
    #[inline]
    pub const fn from_nanos(nanos: u64) -> Duration {
        Duration(Some(time::Duration::from_nanos(nanos)))
    }

    /// Returns the number of _whole_ seconds contained by this `Duration`.
    ///
    /// The returned value does not include the fractional (nanosecond) part of the
    /// duration, which can be obtained using [`subsec_nanos`].
    ///
    /// [`subsec_nanos`]: #method.subsec_nanos
    #[inline]
    pub fn as_secs(&self) -> Option<u64> {
        self.0.map(|d| d.as_secs())
    }

    /// Returns the fractional part of this `Duration`, in whole milliseconds.
    ///
    /// This method does **not** return the length of the duration when
    /// represented by milliseconds. The returned number always represents a
    /// fractional portion of a second (i.e., it is less than one thousand).
    #[inline]
    pub fn subsec_millis(&self) -> Option<u32> {
        self.0.map(|d| d.subsec_millis())
    }

    /// Returns the fractional part of this `Duration`, in whole microseconds.
    ///
    /// This method does **not** return the length of the duration when
    /// represented by microseconds. The returned number always represents a
    /// fractional portion of a second (i.e., it is less than one million).
    #[inline]
    pub fn subsec_micros(&self) -> Option<u32> {
        self.0.map(|d| d.subsec_micros())
    }

    /// Returns the fractional part of this `Duration`, in nanoseconds.
    ///
    /// This method does **not** return the length of the duration when
    /// represented by nanoseconds. The returned number always represents a
    /// fractional portion of a second (i.e., it is less than one billion).
    #[inline]
    pub fn subsec_nanos(&self) -> Option<u32> {
        self.0.map(|d| d.subsec_nanos())
    }

    /// Returns the total number of whole milliseconds contained by this `Duration`.
    #[inline]
    pub fn as_millis(&self) -> Option<u128> {
        self.0.map(|d| d.as_millis())
    }

    /// Returns the total number of whole microseconds contained by this `Duration`.
    #[inline]
    pub fn as_micros(&self) -> Option<u128> {
        self.0.map(|d| d.as_micros())
    }

    /// Returns the total number of nanoseconds contained by this `Duration`.
    #[inline]
    pub fn as_nanos(&self) -> Option<u128> {
        self.0.map(|d| d.as_nanos())
    }

    // TODO: duration_float https://github.com/rust-lang/rust/issues/54361
}

// =============================================================================
// Option based method implementations

impl Duration {
    /// Returns `true` if [`into_inner`] returns `Some`.
    ///
    /// [`into_inner`]: #method.into_inner
    #[inline]
    pub fn is_some(&self) -> bool {
        self.0.is_some()
    }

    /// Returns `true` if [`into_inner`] returns `None`.
    ///
    /// [`into_inner`]: #method.into_inner
    #[inline]
    pub fn is_none(&self) -> bool {
        self.0.is_none()
    }

    /// Returns the contained [`std::time::Duration`] or [`None`].
    ///
    /// [`std::time::Duration`]: std::time::Duration
    #[inline]
    pub const fn into_inner(self) -> Option<time::Duration> {
        self.0
    }

    /// Returns the contained [`std::time::Duration`] or a default.
    ///
    /// `dur.unwrap_or(default)` is equivalent to `dur.into_inner().unwrap_or(default)`.
    ///
    /// [`std::time::Duration`]: std::time::Duration
    #[inline]
    pub fn unwrap_or(self, default: time::Duration) -> time::Duration {
        self.0.unwrap_or(default)
    }

    /// Returns the contained [`std::time::Duration`] or computes it from a closure.
    ///
    /// `dur.unwrap_or_else(default)` is equivalent to `dur.into_inner().unwrap_or_else(default)`.
    ///
    /// [`std::time::Duration`]: std::time::Duration
    #[inline]
    pub fn unwrap_or_else<F>(self, default: F) -> time::Duration
    where
        F: FnOnce() -> time::Duration,
    {
        self.0.unwrap_or_else(default)
    }
}

// =============================================================================
// Trait implementations

impl Default for Duration {
    fn default() -> Duration {
        Duration(Some(time::Duration::default()))
    }
}

impl From<time::Duration> for Duration {
    fn from(dur: time::Duration) -> Duration {
        Duration(Some(dur))
    }
}

impl Add for Duration {
    type Output = Duration;
    fn add(self, rhs: Duration) -> Duration {
        Duration(pair_and_then(self.0, rhs.0, |lhs, rhs| {
            lhs.checked_add(rhs)
        }))
    }
}

impl Add<time::Duration> for Duration {
    type Output = Duration;
    fn add(self, rhs: time::Duration) -> Duration {
        Duration(self.0.and_then(|lhs| lhs.checked_add(rhs)))
    }
}

impl AddAssign for Duration {
    fn add_assign(&mut self, rhs: Duration) {
        *self = *self + rhs;
    }
}

impl AddAssign<time::Duration> for Duration {
    fn add_assign(&mut self, rhs: time::Duration) {
        *self = *self + rhs;
    }
}

impl Sub for Duration {
    type Output = Duration;
    fn sub(self, rhs: Duration) -> Duration {
        Duration(pair_and_then(self.0, rhs.0, |lhs, rhs| {
            lhs.checked_sub(rhs)
        }))
    }
}

impl Sub<time::Duration> for Duration {
    type Output = Duration;
    fn sub(self, rhs: time::Duration) -> Duration {
        Duration(self.0.and_then(|lhs| lhs.checked_sub(rhs)))
    }
}

impl SubAssign for Duration {
    fn sub_assign(&mut self, rhs: Duration) {
        *self = *self - rhs;
    }
}

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

impl Mul<u32> for Duration {
    type Output = Duration;
    fn mul(self, rhs: u32) -> Duration {
        Duration(self.0.and_then(|lhs| lhs.checked_mul(rhs)))
    }
}

impl Mul<Duration> for u32 {
    type Output = Duration;
    fn mul(self, rhs: Duration) -> Duration {
        rhs * self
    }
}

impl MulAssign<u32> for Duration {
    fn mul_assign(&mut self, rhs: u32) {
        *self = *self * rhs;
    }
}

impl Div<u32> for Duration {
    type Output = Duration;

    fn div(self, rhs: u32) -> Duration {
        Duration(self.0.and_then(|lhs| lhs.checked_div(rhs)))
    }
}

impl DivAssign<u32> for Duration {
    fn div_assign(&mut self, rhs: u32) {
        *self = *self / rhs;
    }
}

// TODO: duration_sum
// impl Sum for Duration
// impl<'a> Sum<&'a Duration> for Duration