From 0995d40387dc36bfd6881c99f4772274051bb0f5 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Thu, 27 Jul 2023 16:21:20 +0200 Subject: [PATCH] Make all methods on `Duration` const --- src/duration.rs | 77 +++++++++++++++++++++++++++---------------------- 1 file changed, 43 insertions(+), 34 deletions(-) diff --git a/src/duration.rs b/src/duration.rs index 9439b0bb67..a7656b48d3 100644 --- a/src/duration.rs +++ b/src/duration.rs @@ -16,6 +16,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}; @@ -75,16 +77,16 @@ 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 { - weeks.checked_mul(SECS_PER_WEEK).and_then(Duration::try_seconds) + pub const fn try_weeks(weeks: i64) -> Option { + Duration::try_seconds(try_opt!(weeks.checked_mul(SECS_PER_WEEK))) } /// Makes a new `Duration` with given number of days. @@ -92,16 +94,16 @@ impl Duration { /// 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 fn try_days(days: i64) -> Option { - days.checked_mul(SECS_PER_DAY).and_then(Duration::try_seconds) + pub const fn try_days(days: i64) -> Option { + Duration::try_seconds(try_opt!(days.checked_mul(SECS_PER_DAY))) } /// Makes a new `Duration` with given number of hours. @@ -109,16 +111,16 @@ impl Duration { /// 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 { - hours.checked_mul(SECS_PER_HOUR).and_then(Duration::try_seconds) + pub const fn try_hours(hours: i64) -> Option { + Duration::try_seconds(try_opt!(hours.checked_mul(SECS_PER_HOUR))) } /// Makes a new `Duration` with given number of minutes. @@ -126,16 +128,16 @@ impl Duration { /// 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 { - minutes.checked_mul(SECS_PER_MINUTE).and_then(Duration::try_seconds) + pub const fn try_minutes(minutes: i64) -> Option { + Duration::try_seconds(try_opt!(minutes.checked_mul(SECS_PER_MINUTE))) } /// Makes a new `Duration` with given number of seconds. @@ -143,20 +145,20 @@ impl Duration { /// or less than `i64::MIN` milliseconds. #[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 { + pub const fn try_seconds(seconds: i64) -> Option { 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. @@ -253,7 +255,7 @@ impl Duration { /// Add two durations, returning `None` if overflow occurred. #[must_use] - pub fn checked_add(&self, rhs: &Duration) -> Option { + pub const fn checked_add(&self, rhs: &Duration) -> Option { let mut secs = try_opt!(self.secs.checked_add(rhs.secs)); let mut nanos = self.nanos + rhs.nanos; if nanos >= NANOS_PER_SEC { @@ -263,16 +265,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 { + pub const fn checked_sub(&self, rhs: &Duration) -> Option { let mut secs = try_opt!(self.secs.checked_sub(rhs.secs)); let mut nanos = self.nanos - rhs.nanos; if nanos < 0 { @@ -282,10 +283,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), } } @@ -327,13 +327,13 @@ impl Duration { /// /// This function errors when original duration is larger than the maximum /// value supported for this type. - pub fn from_std(duration: StdDuration) -> Result { + pub const fn from_std(duration: StdDuration) -> Result { // 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) @@ -343,12 +343,21 @@ 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+ pub fn to_std(&self) -> Result { if self.secs < 0 { return Err(OutOfRangeError(())); } Ok(StdDuration::new(self.secs as u64, self.nanos as u32)) } + + /// Returns `true` 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 {