spetz commented on code in PR #3891:
URL: https://github.com/apache/iggy/pull/3891#discussion_r3854400534
##########
core/common/src/utils/duration.rs:
##########
@@ -226,6 +227,221 @@ impl Visitor<'_> for IggyDurationVisitor {
}
}
+/// A duration that is guaranteed to be at least one microsecond.
+///
+/// `IggyDuration::from_str` maps `0`, `none`, `disabled` and `unlimited` to
the same
+/// zero, so all four are rejected here. Serialization emits whole
microseconds, so a
+/// shorter duration such as `1ns` is rejected as well.
+///
+/// # Example
+///
+/// ```
+/// use iggy_common::{IggyDuration, NonZeroIggyDuration, NonZeroDurationError};
+/// use std::str::FromStr;
+///
+/// let interval = NonZeroIggyDuration::from_str("1s").unwrap();
+/// assert_eq!(1, interval.as_secs());
+/// assert_eq!("1s", format!("{}", interval));
+///
+/// assert_eq!(Err(NonZeroDurationError::Zero),
NonZeroIggyDuration::from_str("none"));
+/// assert_eq!(
+/// Err(NonZeroDurationError::Zero),
+/// NonZeroIggyDuration::try_from(IggyDuration::from(0_u64)),
+/// );
+/// assert_eq!(
+/// Err(NonZeroDurationError::SubMicrosecond),
+/// NonZeroIggyDuration::from_str("1ns"),
+/// );
+/// ```
+#[derive(Debug, Clone, Copy, Eq, PartialEq)]
+pub struct NonZeroIggyDuration {
+ duration: IggyDuration,
+}
+
+/// The reason a value could not become a `NonZeroIggyDuration`.
+#[derive(Debug, Clone, PartialEq)]
+pub enum NonZeroDurationError {
+ /// The value parsed or converted to zero.
+ Zero,
+ /// The value is shorter than the one microsecond resolution of the wire
format.
+ SubMicrosecond,
+ /// The text is not a duration `humantime` understands.
+ InvalidFormat(humantime::DurationError),
+}
+
+impl NonZeroIggyDuration {
+ pub const ONE_SECOND: NonZeroIggyDuration = NonZeroIggyDuration {
+ duration: IggyDuration::ONE_SECOND,
+ };
+
+ pub fn new(duration: Duration) -> Result<Self, NonZeroDurationError> {
+ IggyDuration::new(duration).try_into()
+ }
+
+ pub fn get(&self) -> IggyDuration {
+ self.duration
+ }
+
+ pub fn get_duration(&self) -> Duration {
+ self.duration.get_duration()
+ }
+
+ pub fn as_human_time_string(&self) -> String {
+ self.duration.as_human_time_string()
+ }
+
+ pub fn as_secs(&self) -> u32 {
+ self.duration.as_secs()
+ }
+
+ pub fn as_secs_f64(&self) -> f64 {
+ self.duration.as_secs_f64()
+ }
+
+ pub fn as_micros(&self) -> u64 {
+ self.duration.as_micros()
+ }
+
+ /// The gap between two non-zero durations is zero when they are equal, so
the
+ /// result is an `IggyDuration`.
+ pub fn abs_diff(&self, other: NonZeroIggyDuration) -> IggyDuration {
+ self.duration.abs_diff(other.duration)
+ }
+}
+
+impl Display for NonZeroDurationError {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ match self {
+ NonZeroDurationError::Zero => write!(f, "duration must be greater
than zero"),
+ NonZeroDurationError::SubMicrosecond => {
+ write!(f, "duration must be at least one microsecond")
+ }
+ NonZeroDurationError::InvalidFormat(error) => write!(f, "invalid
duration: {error}"),
+ }
+ }
+}
+
+impl Error for NonZeroDurationError {
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ match self {
+ NonZeroDurationError::Zero | NonZeroDurationError::SubMicrosecond
=> None,
+ NonZeroDurationError::InvalidFormat(error) => Some(error),
+ }
+ }
+}
+
+impl From<humantime::DurationError> for NonZeroDurationError {
+ fn from(error: humantime::DurationError) -> Self {
+ NonZeroDurationError::InvalidFormat(error)
+ }
+}
+
+impl TryFrom<IggyDuration> for NonZeroIggyDuration {
+ type Error = NonZeroDurationError;
+
+ fn try_from(duration: IggyDuration) -> Result<Self, Self::Error> {
+ if duration.is_zero() {
+ return Err(NonZeroDurationError::Zero);
+ }
+
+ // Serialization emits whole microseconds, so a shorter duration would
come back as zero.
+ if duration.as_micros() == 0 {
Review Comment:
`IggyDuration::as_micros()` already truncates the underlying `u128` value to
`u64`. A duration of `2^64 + 1` microseconds passes this check but serializes
as `1`, while exactly `2^64` is incorrectly classified as `SubMicrosecond`.
Please compare the underlying `Duration` against `1us` here and make
serialization reject values above `u64::MAX` microseconds, with boundary tests.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]