Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proto: Align TryFrom impls for Duration with prost-types #1456

Merged
merged 2 commits into from
Aug 8, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading