diff --git a/proto/src/google/protobuf/duration.rs b/proto/src/google/protobuf/duration.rs index b684abc32..c14957511 100644 --- a/proto/src/google/protobuf/duration.rs +++ b/proto/src/google/protobuf/duration.rs @@ -5,6 +5,7 @@ // Copyright (c) 2020 InfluxData use core::convert::TryFrom; +use core::fmt; use prost::Name; @@ -94,48 +95,72 @@ impl Duration { } } -/// Converts a `core::time::Duration` to a `Duration`. -impl From 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 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 { + /// Converts a `Duration` to a `core::time::Duration`, failing if the duration is negative. + fn try_from(mut duration: Duration) -> Result { 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 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 { + 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(&self, serializer: S) -> Result where