Skip to content

Commit

Permalink
proto: Align TryFrom impls for Duration with prost-types (#1456)
Browse files Browse the repository at this point in the history
* Fix `TryFrom` impl to return a `DurationError` as per `prost-types`
* Remove conflicting `From<core::time::Duration>` impl
  • Loading branch information
romac authored Aug 8, 2024
1 parent 685d104 commit f1ebab8
Showing 1 changed file with 50 additions and 25 deletions.
75 changes: 50 additions & 25 deletions proto/src/google/protobuf/duration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// Copyright (c) 2020 InfluxData

use core::convert::TryFrom;
use core::fmt;

use prost::Name;

Expand Down Expand Up @@ -94,48 +95,72 @@ impl Duration {
}
}

/// Converts a `core::time::Duration` to a `Duration`.
impl From<core::time::Duration> for Duration {
fn from(duration: core::time::Duration) -> Duration {
let seconds = duration.as_secs();
let seconds = if seconds > i64::MAX as u64 {
i64::MAX
} else {
seconds as i64
};
let nanos = duration.subsec_nanos();
let nanos = if nanos > i32::MAX as u32 {
i32::MAX
} else {
nanos as i32
};
let mut duration = Duration { seconds, nanos };
duration.normalize();
duration
/// A duration handling error.
#[derive(Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum DurationError {
/// Indicates failure to convert a [`Duration`] to a [`core::time::Duration`] because
/// the duration is negative. The included [`core::time::Duration`] matches the magnitude of the
/// original negative [`Duration`].
NegativeDuration(core::time::Duration),

/// Indicates failure to convert a [`core::time::Duration`] to a [`Duration`].
///
/// Converting a [`core::time::Duration`] to a [`Duration`] fails if the magnitude
/// exceeds that representable by [`Duration`].
OutOfRange,
}

impl fmt::Display for DurationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DurationError::NegativeDuration(duration) => {
write!(f, "failed to convert negative duration: {duration:?}")
},
DurationError::OutOfRange => {
write!(f, "failed to convert duration out of range")
},
}
}
}

#[cfg(feature = "std")]
impl std::error::Error for DurationError {}

impl TryFrom<Duration> for core::time::Duration {
type Error = core::time::Duration;
type Error = DurationError;

/// Converts a `Duration` to a result containing a positive (`Ok`) or negative (`Err`)
/// `std::time::Duration`.
fn try_from(mut duration: Duration) -> Result<core::time::Duration, core::time::Duration> {
/// Converts a `Duration` to a `core::time::Duration`, failing if the duration is negative.
fn try_from(mut duration: Duration) -> Result<core::time::Duration, DurationError> {
duration.normalize();
if duration.seconds >= 0 {
if duration.seconds >= 0 && duration.nanos >= 0 {
Ok(core::time::Duration::new(
duration.seconds as u64,
duration.nanos as u32,
))
} else {
Err(core::time::Duration::new(
Err(DurationError::NegativeDuration(core::time::Duration::new(
(-duration.seconds) as u64,
(-duration.nanos) as u32,
))
)))
}
}
}

impl TryFrom<core::time::Duration> for Duration {
type Error = DurationError;

/// Converts a `core::time::Duration` to a `Duration`, failing if the duration is too large.
fn try_from(duration: core::time::Duration) -> Result<Duration, DurationError> {
let seconds = i64::try_from(duration.as_secs()).map_err(|_| DurationError::OutOfRange)?;
let nanos = duration.subsec_nanos() as i32;

let mut duration = Duration { seconds, nanos };
duration.normalize();
Ok(duration)
}
}

impl serde::Serialize for Duration {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
Expand Down

0 comments on commit f1ebab8

Please sign in to comment.