diff --git a/serde_with/src/utils/duration.rs b/serde_with/src/utils/duration.rs index 91184b0f..dca876ad 100644 --- a/serde_with/src/utils/duration.rs +++ b/serde_with/src/utils/duration.rs @@ -28,13 +28,17 @@ impl Sign { *self == Sign::Negative } - pub(crate) fn apply(&self, value: T) -> T - where - T: core::ops::Neg, - { + pub(crate) fn apply_f64(&self, value: f64) -> f64 { match *self { Sign::Positive => value, - Sign::Negative => value.neg(), + Sign::Negative => -value, + } + } + + pub(crate) fn apply_i64(&self, value: i64) -> Option { + match *self { + Sign::Positive => Some(value), + Sign::Negative => value.checked_neg(), } } } @@ -53,6 +57,16 @@ impl DurationSigned { } } + pub(crate) fn checked_mul(mut self, rhs: u32) -> Option { + self.duration = self.duration.checked_mul(rhs)?; + Some(self) + } + + pub(crate) fn checked_div(mut self, rhs: u32) -> Option { + self.duration = self.duration.checked_div(rhs)?; + Some(self) + } + #[cfg(any(feature = "chrono_0_4", feature = "time_0_3"))] pub(crate) fn with_duration(sign: Sign, duration: Duration) -> Self { Self { sign, duration } @@ -107,24 +121,6 @@ impl From<&SystemTime> for DurationSigned { } } -impl core::ops::Mul for DurationSigned { - type Output = DurationSigned; - - fn mul(mut self, rhs: u32) -> Self::Output { - self.duration *= rhs; - self - } -} - -impl core::ops::Div for DurationSigned { - type Output = DurationSigned; - - fn div(mut self, rhs: u32) -> Self::Output { - self.duration /= rhs; - self - } -} - impl SerializeAs for DurationSeconds where STRICTNESS: Strictness, @@ -163,11 +159,15 @@ where { let mut secs = source .sign - .apply(i64::try_from(source.duration.as_secs()).map_err(|_| { + .apply_i64(i64::try_from(source.duration.as_secs()).map_err(|_| { SerError::custom("The Duration of Timestamp is outside the supported range.") - })?); + })?) + .ok_or_else(|| { + S::Error::custom("The Duration of Timestamp is outside the supported range.") + })?; // Properly round the value + // TODO check for overflows BUG771 if source.duration.subsec_millis() >= 500 { if source.sign.is_positive() { secs += 1; @@ -189,7 +189,7 @@ where { // as conversions are necessary for floats #[allow(clippy::as_conversions)] - let mut secs = source.sign.apply(source.duration.as_secs() as f64); + let mut secs = source.sign.apply_f64(source.duration.as_secs() as f64); // Properly round the value if source.duration.subsec_millis() >= 500 { @@ -214,9 +214,12 @@ where { let mut secs = source .sign - .apply(i64::try_from(source.duration.as_secs()).map_err(|_| { + .apply_i64(i64::try_from(source.duration.as_secs()).map_err(|_| { SerError::custom("The Duration of Timestamp is outside the supported range.") - })?); + })?) + .ok_or_else(|| { + S::Error::custom("The Duration of Timestamp is outside the supported range.") + })?; // Properly round the value if source.duration.subsec_millis() >= 500 { @@ -240,7 +243,7 @@ where { source .sign - .apply(source.duration.as_secs_f64()) + .apply_f64(source.duration.as_secs_f64()) .serialize(serializer) } } @@ -256,7 +259,7 @@ where { source .sign - .apply(source.duration.as_secs_f64()) + .apply_f64(source.duration.as_secs_f64()) .to_string() .serialize(serializer) } @@ -276,7 +279,8 @@ macro_rules! duration_impls { where S: Serializer, { - $inner::::serialize_as(&(*source * $factor), serializer) + let value = source.checked_mul($factor).ok_or_else(|| S::Error::custom("Failed to serialize value as the value cannot be represented."))?; + $inner::::serialize_as(&value, serializer) } } @@ -291,7 +295,8 @@ macro_rules! duration_impls { D: Deserializer<'de>, { let dur = $inner::::deserialize_as(deserializer)?; - Ok(dur / $factor) + let dur = dur.checked_div($factor).ok_or_else(|| D::Error::custom("Failed to deserialize value as the value cannot be represented."))?; + Ok(dur) } }