Skip to content

Commit

Permalink
Make all methods on Duration const
Browse files Browse the repository at this point in the history
  • Loading branch information
pitdicker committed Jul 27, 2023
1 parent 390db3b commit 5f63149
Showing 1 changed file with 42 additions and 33 deletions.
75 changes: 42 additions & 33 deletions src/duration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ use core::{fmt, i64};
#[cfg(feature = "std")]
use std::error::Error;

use crate::{expect, try_opt};

#[cfg(feature = "rkyv")]
use rkyv::{Archive, Deserialize, Serialize};

Expand Down Expand Up @@ -76,88 +78,88 @@ impl Duration {
/// Panics when the duration is out of bounds.
#[inline]
#[must_use]
pub fn weeks(weeks: i64) -> Duration {
Duration::try_weeks(weeks).expect("Duration::weeks out of bounds")
pub const fn weeks(weeks: i64) -> Duration {
expect!(Duration::try_weeks(weeks), "Duration::weeks out of bounds")
}

/// Makes a new `Duration` with given number of weeks.
/// Equivalent to `Duration::seconds(weeks * 7 * 24 * 60 * 60)` with overflow checks.
/// Returns None when the duration is out of bounds.
#[inline]
pub fn try_weeks(weeks: i64) -> Option<Duration> {
weeks.checked_mul(SECS_PER_WEEK).and_then(Duration::try_seconds)
pub const fn try_weeks(weeks: i64) -> Option<Duration> {
Duration::try_seconds(try_opt!(weeks.checked_mul(SECS_PER_WEEK)))
}

/// Makes a new `Duration` with given number of days.
/// Equivalent to `Duration::seconds(days * 24 * 60 * 60)` with overflow checks.
/// Panics when the duration is out of bounds.
#[inline]
#[must_use]
pub fn days(days: i64) -> Duration {
Duration::try_days(days).expect("Duration::days out of bounds")
pub const fn days(days: i64) -> Duration {
expect!(Duration::try_days(days), "Duration::days out of bounds")
}

/// Makes a new `Duration` with given number of days.
/// Equivalent to `Duration::seconds(days * 24 * 60 * 60)` with overflow checks.
/// Returns None when the duration is out of bounds.
#[inline]
pub const fn try_days(days: i64) -> Option<Duration> {
Duration::try_seconds(unwrap_opt!(days.checked_mul(SECS_PER_DAY)))
Duration::try_seconds(try_opt!(days.checked_mul(SECS_PER_DAY)))
}

/// Makes a new `Duration` with given number of hours.
/// Equivalent to `Duration::seconds(hours * 60 * 60)` with overflow checks.
/// Panics when the duration is out of bounds.
#[inline]
#[must_use]
pub fn hours(hours: i64) -> Duration {
Duration::try_hours(hours).expect("Duration::hours ouf of bounds")
pub const fn hours(hours: i64) -> Duration {
expect!(Duration::try_hours(hours), "Duration::hours ouf of bounds")
}

/// Makes a new `Duration` with given number of hours.
/// Equivalent to `Duration::seconds(hours * 60 * 60)` with overflow checks.
/// Returns None when the duration is out of bounds.
#[inline]
pub fn try_hours(hours: i64) -> Option<Duration> {
hours.checked_mul(SECS_PER_HOUR).and_then(Duration::try_seconds)
pub const fn try_hours(hours: i64) -> Option<Duration> {
Duration::try_seconds(try_opt!(hours.checked_mul(SECS_PER_HOUR)))
}

/// Makes a new `Duration` with given number of minutes.
/// Equivalent to `Duration::seconds(minutes * 60)` with overflow checks.
/// Panics when the duration is out of bounds.
#[inline]
#[must_use]
pub fn minutes(minutes: i64) -> Duration {
Duration::try_minutes(minutes).expect("Duration::minutes out of bounds")
pub const fn minutes(minutes: i64) -> Duration {
expect!(Duration::try_minutes(minutes), "Duration::minutes out of bounds")
}

/// Makes a new `Duration` with given number of minutes.
/// Equivalent to `Duration::seconds(minutes * 60)` with overflow checks.
/// Returns None when the duration is out of bounds.
#[inline]
pub fn try_minutes(minutes: i64) -> Option<Duration> {
minutes.checked_mul(SECS_PER_MINUTE).and_then(Duration::try_seconds)
pub const fn try_minutes(minutes: i64) -> Option<Duration> {
Duration::try_seconds(try_opt!(minutes.checked_mul(SECS_PER_MINUTE)))
}

/// Makes a new `Duration` with given number of seconds.
/// Panics when the duration is more than `i64::MAX` seconds
/// or less than `i64::MIN` seconds.
#[inline]
#[must_use]
pub fn seconds(seconds: i64) -> Duration {
Duration::try_seconds(seconds).expect("Duration::seconds out of bounds")
pub const fn seconds(seconds: i64) -> Duration {
expect!(Duration::try_seconds(seconds), "Duration::seconds out of bounds")
}

/// Makes a new `Duration` with given number of seconds.
/// Returns None when the duration is more than `i64::MAX` milliseconds
/// or less than `i64::MIN` milliseconds.
#[inline]
pub fn try_seconds(seconds: i64) -> Option<Duration> {
pub const fn try_seconds(seconds: i64) -> Option<Duration> {
let d = Duration { secs: seconds, nanos: 0 };
if d < MIN || d > MAX {
return None;
match d.out_of_bounds() {
true => None,
false => Some(d),
}
Some(d)
}

/// Makes a new `Duration` with given number of milliseconds.
Expand Down Expand Up @@ -254,7 +256,7 @@ impl Duration {

/// Add two durations, returning `None` if overflow occurred.
#[must_use]
pub fn checked_add(&self, rhs: &Duration) -> Option<Duration> {
pub const fn checked_add(&self, rhs: &Duration) -> Option<Duration> {
let mut secs = try_opt!(self.secs.checked_add(rhs.secs));
let mut nanos = self.nanos + rhs.nanos;
if nanos >= NANOS_PER_SEC {
Expand All @@ -264,16 +266,15 @@ impl Duration {
let d = Duration { secs, nanos };
// Even if d is within the bounds of i64 seconds,
// it might still overflow i64 milliseconds.
if d < MIN || d > MAX {
None
} else {
Some(d)
match d.out_of_bounds() {
true => None,
false => Some(d),
}
}

/// Subtract two durations, returning `None` if overflow occurred.
#[must_use]
pub fn checked_sub(&self, rhs: &Duration) -> Option<Duration> {
pub const fn checked_sub(&self, rhs: &Duration) -> Option<Duration> {
let mut secs = try_opt!(self.secs.checked_sub(rhs.secs));
let mut nanos = self.nanos - rhs.nanos;
if nanos < 0 {
Expand All @@ -283,10 +284,9 @@ impl Duration {
let d = Duration { secs, nanos };
// Even if d is within the bounds of i64 seconds,
// it might still overflow i64 milliseconds.
if d < MIN || d > MAX {
None
} else {
Some(d)
match d.out_of_bounds() {
true => None,
false => Some(d),
}
}

Expand Down Expand Up @@ -329,13 +329,13 @@ impl Duration {
/// This function errors when original duration is larger than the maximum
/// value supported for this type.
#[cfg(feature = "std")]
pub fn from_std(duration: StdDuration) -> Result<Duration, OutOfRangeError> {
pub const fn from_std(duration: StdDuration) -> Result<Duration, OutOfRangeError> {
// We need to check secs as u64 before coercing to i64
if duration.as_secs() > MAX.secs as u64 {
return Err(OutOfRangeError(()));
}
let d = Duration { secs: duration.as_secs() as i64, nanos: duration.subsec_nanos() as i32 };
if d > MAX {
if d.out_of_bounds() {
return Err(OutOfRangeError(()));
}
Ok(d)
Expand All @@ -345,13 +345,22 @@ impl Duration {
///
/// This function errors when duration is less than zero. As standard
/// library implementation is limited to non-negative values.
// TODO: make this method const once our MSRV is 1.58+
#[cfg(feature = "std")]
pub fn to_std(&self) -> Result<StdDuration, OutOfRangeError> {
if self.secs < 0 {
return Err(OutOfRangeError(()));
}
Ok(StdDuration::new(self.secs as u64, self.nanos as u32))
}

/// Returns true if and only if d < MIN || d > MAX
const fn out_of_bounds(&self) -> bool {
self.secs < MIN.secs
|| self.secs > MAX.secs
|| (self.secs == MAX.secs && self.nanos > MAX.nanos)
|| (self.secs == MIN.secs && self.nanos < MIN.nanos)
}
}

impl Neg for Duration {
Expand Down

0 comments on commit 5f63149

Please sign in to comment.