stdext 0.1.0

Extensions for the Rust standard library structures.
Documentation
//! Extension traits for `std::time::Duration`.

use std::time::Duration;

const SECS_IN_MIN: u64 = 60;
const SECS_IN_HOUR: u64 = 3600;
const SECS_IN_DAY: u64 = 3600 * 24;

/// Extension trait with useful methods for [`std::time::Duration`].
///
/// [`std::time::Duration`]: https://doc.rust-lang.org/std/time/struct.Duration.html
pub trait DurationExt {
    /// Creates a new `Duration` from the specified number of minutes.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::time::Duration;
    /// use stdext::prelude::*;
    ///
    /// let duration = Duration::from_minutes(1);
    ///
    /// assert_eq!(duration, Duration::from_secs(60));
    /// ```
    ///
    /// # Panics
    ///
    /// Panics if the total amount of seconds exceeds the `u64` type range.
    fn from_minutes(minutes: u64) -> Duration;

    /// Creates a new `Duration` from the specified number of hours.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::time::Duration;
    /// use stdext::prelude::*;
    ///
    /// let duration = Duration::from_hours(1);
    ///
    /// assert_eq!(duration, Duration::from_secs(3600));
    /// ```
    ///
    /// # Panics
    ///
    /// Panics if the total amount of seconds exceeds the `u64` type range.
    fn from_hours(hours: u64) -> Duration;

    /// Creates a new `Duration` from the specified number of hours.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::time::Duration;
    /// use stdext::prelude::*;
    ///
    /// let duration = Duration::from_days(1);
    ///
    /// assert_eq!(duration, Duration::from_secs(3600 * 24));
    /// ```
    ///
    /// # Panics
    ///
    /// Panics if the total amount of seconds exceeds the `u64` type range.
    fn from_days(days: u64) -> Duration;

    /// Adds the specified amount of nanoseconds to the `Duration` object.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::time::Duration;
    /// use stdext::prelude::*;
    ///
    /// let duration = Duration::default().add_nanos(1);
    ///
    /// assert_eq!(duration, Duration::default() + Duration::from_nanos(1));
    /// ```
    fn add_nanos(self, nanos: u64) -> Duration;

    /// Adds the specified amount of microseconds to the `Duration` object.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::time::Duration;
    /// use stdext::prelude::*;
    ///
    /// let duration = Duration::default().add_micros(1);
    ///
    /// assert_eq!(duration, Duration::default() + Duration::from_micros(1));
    /// ```
    fn add_micros(self, micros: u64) -> Duration;

    /// Adds the specified amount of milliseconds to the `Duration` object.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::time::Duration;
    /// use stdext::prelude::*;
    ///
    /// let duration = Duration::default().add_millis(1);
    ///
    /// assert_eq!(duration, Duration::default() + Duration::from_millis(1));
    /// ```
    fn add_millis(self, millis: u64) -> Duration;

    /// Adds the specified amount of seconds to the `Duration` object.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::time::Duration;
    /// use stdext::prelude::*;
    ///
    /// let duration = Duration::default().add_secs(1);
    ///
    /// assert_eq!(duration, Duration::default() + Duration::from_secs(1));
    /// ```
    fn add_secs(self, seconds: u64) -> Duration;

    /// Adds the specified amount of minutes to the `Duration` object.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::time::Duration;
    /// use stdext::prelude::*;
    ///
    /// let duration = Duration::default().add_minutes(1);
    ///
    /// assert_eq!(duration, Duration::default() + Duration::from_minutes(1));
    /// ```
    fn add_minutes(self, minutes: u64) -> Duration;

    /// Adds the specified amount of hours to the `Duration` object.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::time::Duration;
    /// use stdext::prelude::*;
    ///
    /// let duration = Duration::default().add_hours(1);
    ///
    /// assert_eq!(duration, Duration::default() + Duration::from_hours(1));
    /// ```
    fn add_hours(self, hours: u64) -> Duration;

    /// Adds the specified amount of days to the `Duration` object.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::time::Duration;
    /// use stdext::prelude::*;
    ///
    /// let duration = Duration::default().add_days(1);
    ///
    /// assert_eq!(duration, Duration::default() + Duration::from_days(1));
    /// ```
    fn add_days(self, days: u64) -> Duration;

    /// Returns the number of _whole_ minutes contained by this `Duration`.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::time::Duration;
    /// use stdext::prelude::*;
    ///
    /// let duration = Duration::new(61, 730023852);
    /// assert_eq!(duration.as_minutes(), 1);
    /// ```
    fn as_minutes(&self) -> u64;

    /// Returns the number of _whole_ hours contained by this `Duration`.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::time::Duration;
    /// use stdext::prelude::*;
    ///
    /// let duration = Duration::new(3601, 730023852);
    /// assert_eq!(duration.as_hours(), 1);
    /// ```
    fn as_hours(&self) -> u64;

    /// Returns the number of _whole_ hours contained by this `Duration`.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::time::Duration;
    /// use stdext::prelude::*;
    ///
    /// let duration = Duration::new(61, 730023852).add_days(1);
    /// assert_eq!(duration.as_days(), 1);
    /// ```
    fn as_days(&self) -> u64;
}

impl DurationExt for Duration {
    fn from_minutes(minutes: u64) -> Self {
        let seconds = minutes * SECS_IN_MIN;
        Self::from_secs(seconds)
    }

    fn from_hours(hours: u64) -> Self {
        let seconds: u64 = hours * SECS_IN_HOUR;
        Self::from_secs(seconds)
    }

    fn from_days(days: u64) -> Self {
        let seconds = days * SECS_IN_DAY;
        Self::from_secs(seconds)
    }

    fn add_nanos(self, nanos: u64) -> Self {
        self + Self::from_nanos(nanos)
    }

    fn add_micros(self, micros: u64) -> Self {
        self + Self::from_micros(micros)
    }

    fn add_millis(self, millis: u64) -> Self {
        self + Self::from_millis(millis)
    }

    fn add_secs(self, seconds: u64) -> Self {
        self + Self::from_secs(seconds)
    }

    fn add_minutes(self, minutes: u64) -> Self {
        self + Self::from_minutes(minutes)
    }

    fn add_hours(self, hours: u64) -> Self {
        self + Self::from_hours(hours)
    }

    fn add_days(self, days: u64) -> Self {
        self + Self::from_days(days)
    }

    fn as_minutes(&self) -> u64 {
        self.as_secs() / SECS_IN_MIN
    }

    fn as_hours(&self) -> u64 {
        self.as_secs() / SECS_IN_HOUR
    }

    fn as_days(&self) -> u64 {
        self.as_secs() / SECS_IN_DAY
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn from_methods() {
        // Check `from_minutes`
        let test_vector = vec![0, 1, u64::max_value() / SECS_IN_MIN];
        for minutes in test_vector {
            let seconds = minutes * SECS_IN_MIN;
            assert_eq!(
                Duration::from_minutes(minutes),
                Duration::from_secs(seconds)
            );
        }

        // Check `from_hours`
        let test_vector = vec![0, 1, u64::max_value() / SECS_IN_HOUR];
        for hours in test_vector {
            let seconds = hours * SECS_IN_HOUR;
            assert_eq!(Duration::from_hours(hours), Duration::from_secs(seconds));
        }

        // Check `from_days`
        let test_vector = vec![0, 1, u64::max_value() / SECS_IN_DAY];
        for days in test_vector {
            let seconds = days * SECS_IN_DAY;
            assert_eq!(Duration::from_days(days), Duration::from_secs(seconds));
        }
    }

    #[test]
    fn add_methods() {
        let duration = Duration::default()
            .add_nanos(1)
            .add_micros(1)
            .add_millis(1)
            .add_secs(1)
            .add_minutes(1)
            .add_hours(1)
            .add_days(1);

        let expected_duration = Duration::new(
            1 * SECS_IN_DAY + 1 * SECS_IN_HOUR + 1 * SECS_IN_MIN + 1,
            1 * 1_000_000 + 1 * 1_000 + 1,
        );

        assert_eq!(duration, expected_duration);
    }

    #[test]
    fn as_methods() {
        let test_vector = vec![0, SECS_IN_MIN, SECS_IN_HOUR, SECS_IN_DAY];

        for seconds in test_vector {
            for seconds in &[seconds, seconds + 1, seconds * 2, seconds * 2 + 1] {
                let duration = Duration::from_secs(*seconds);

                assert_eq!(duration.as_minutes(), duration.as_secs() / SECS_IN_MIN);
                assert_eq!(duration.as_hours(), duration.as_secs() / SECS_IN_HOUR);
                assert_eq!(duration.as_days(), duration.as_secs() / SECS_IN_DAY);
            }
        }
    }
}