From 60bf7b767cffd7b40e0c03b2889a14488de20fd4 Mon Sep 17 00:00:00 2001 From: Awpteamoose Date: Wed, 17 Jul 2024 14:15:35 +0100 Subject: [PATCH 1/6] impl serde for timedelta --- src/time_delta.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/time_delta.rs b/src/time_delta.rs index 88aa31c6e..9599c001f 100644 --- a/src/time_delta.rs +++ b/src/time_delta.rs @@ -62,6 +62,29 @@ pub struct TimeDelta { nanos: i32, // Always 0 <= nanos < NANOS_PER_SEC } +#[cfg(feature = "serde")] +impl serde::Serialize for TimeDelta { + fn serialize(&self, serializer: S) -> Result { + <(i64, i32) as serde::Serialize>::serialize(&(self.secs, self.nanos), serializer) + } +} + +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for TimeDelta { + fn deserialize>(deserializer: D) -> Result { + let (secs, nanos) = <(i64, i32) as serde::Deserialize>::deserialize(deserializer)?; + if secs < MIN.secs + || secs > MAX.secs + || nanos >= 1_000_000_000 + || (secs == MAX.secs && nanos > MAX.nanos) + || (secs == MIN.secs && nanos < MIN.nanos) + { + return Err(serde::de::Error::custom("TimeDelta out of bounds")); + } + Ok(TimeDelta { secs, nanos }) + } +} + /// The minimum possible `TimeDelta`: `-i64::MAX` milliseconds. pub(crate) const MIN: TimeDelta = TimeDelta { secs: -i64::MAX / MILLIS_PER_SEC - 1, @@ -1305,4 +1328,11 @@ mod tests { let bytes = rkyv::to_bytes::<_, 16>(&duration).unwrap(); assert_eq!(rkyv::from_bytes::(&bytes).unwrap(), duration); } + + #[test] + #[cfg(feature = "serde")] + fn test_serde() { + let duration = TimeDelta::new(123, 456).unwrap(); + assert_eq!(serde_json::from_value::(serde_json::to_value(&duration).unwrap()).unwrap(), duration); + } } From 6451c9f87d40cf6d40fcb6089f58ba86b65b72c5 Mon Sep 17 00:00:00 2001 From: Awpteamoose Date: Wed, 17 Jul 2024 15:10:28 +0100 Subject: [PATCH 2/6] fmt --- src/time_delta.rs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/time_delta.rs b/src/time_delta.rs index 9599c001f..ee158dcf1 100644 --- a/src/time_delta.rs +++ b/src/time_delta.rs @@ -64,25 +64,24 @@ pub struct TimeDelta { #[cfg(feature = "serde")] impl serde::Serialize for TimeDelta { - fn serialize(&self, serializer: S) -> Result { - <(i64, i32) as serde::Serialize>::serialize(&(self.secs, self.nanos), serializer) - } + fn serialize(&self, serializer: S) -> Result { + <(i64, i32) as serde::Serialize>::serialize(&(self.secs, self.nanos), serializer) + } } #[cfg(feature = "serde")] impl<'de> serde::Deserialize<'de> for TimeDelta { - fn deserialize>(deserializer: D) -> Result { - let (secs, nanos) = <(i64, i32) as serde::Deserialize>::deserialize(deserializer)?; - if secs < MIN.secs - || secs > MAX.secs + fn deserialize>(deserializer: D) -> Result { + let (secs, nanos) = <(i64, i32) as serde::Deserialize>::deserialize(deserializer)?; + if !(MIN.secs..=MAX.secs).contains(&secs) || nanos >= 1_000_000_000 || (secs == MAX.secs && nanos > MAX.nanos) || (secs == MIN.secs && nanos < MIN.nanos) { return Err(serde::de::Error::custom("TimeDelta out of bounds")); } - Ok(TimeDelta { secs, nanos }) - } + Ok(TimeDelta { secs, nanos }) + } } /// The minimum possible `TimeDelta`: `-i64::MAX` milliseconds. @@ -1333,6 +1332,9 @@ mod tests { #[cfg(feature = "serde")] fn test_serde() { let duration = TimeDelta::new(123, 456).unwrap(); - assert_eq!(serde_json::from_value::(serde_json::to_value(&duration).unwrap()).unwrap(), duration); + assert_eq!( + serde_json::from_value::(serde_json::to_value(duration).unwrap()).unwrap(), + duration + ); } } From 35c1ac5912eb7a33f8f5e8e9a27b11c65de7dde5 Mon Sep 17 00:00:00 2001 From: Awpteamoose Date: Wed, 17 Jul 2024 15:20:34 +0100 Subject: [PATCH 3/6] add a panic test --- src/time_delta.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/time_delta.rs b/src/time_delta.rs index ee158dcf1..d0925a840 100644 --- a/src/time_delta.rs +++ b/src/time_delta.rs @@ -1337,4 +1337,11 @@ mod tests { duration ); } + + #[test] + #[cfg(feature = "serde")] + #[should_panic(expected = "TimeDelta out of bounds")] + fn test_serde_oob_panic() { + let _ = serde_json::from_value::(serde_json::json!([MAX.secs + 1, 0])).unwrap(); + } } From 3ac01e362ee5785a920c9bd918c54fa3ef2a94ad Mon Sep 17 00:00:00 2001 From: Awpteamoose Date: Fri, 6 Sep 2024 11:41:32 +0100 Subject: [PATCH 4/6] inline mod --- src/time_delta.rs | 87 ++++++++++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 39 deletions(-) diff --git a/src/time_delta.rs b/src/time_delta.rs index d0925a840..15c72b039 100644 --- a/src/time_delta.rs +++ b/src/time_delta.rs @@ -62,28 +62,6 @@ pub struct TimeDelta { nanos: i32, // Always 0 <= nanos < NANOS_PER_SEC } -#[cfg(feature = "serde")] -impl serde::Serialize for TimeDelta { - fn serialize(&self, serializer: S) -> Result { - <(i64, i32) as serde::Serialize>::serialize(&(self.secs, self.nanos), serializer) - } -} - -#[cfg(feature = "serde")] -impl<'de> serde::Deserialize<'de> for TimeDelta { - fn deserialize>(deserializer: D) -> Result { - let (secs, nanos) = <(i64, i32) as serde::Deserialize>::deserialize(deserializer)?; - if !(MIN.secs..=MAX.secs).contains(&secs) - || nanos >= 1_000_000_000 - || (secs == MAX.secs && nanos > MAX.nanos) - || (secs == MIN.secs && nanos < MIN.nanos) - { - return Err(serde::de::Error::custom("TimeDelta out of bounds")); - } - Ok(TimeDelta { secs, nanos }) - } -} - /// The minimum possible `TimeDelta`: `-i64::MAX` milliseconds. pub(crate) const MIN: TimeDelta = TimeDelta { secs: -i64::MAX / MILLIS_PER_SEC - 1, @@ -660,6 +638,54 @@ impl arbitrary::Arbitrary<'_> for TimeDelta { } } +#[cfg(feature = "serde")] +mod serde { + use super::{TimeDelta, MAX, MIN}; + use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer}; + + impl Serialize for TimeDelta { + fn serialize(&self, serializer: S) -> Result { + <(i64, i32) as Serialize>::serialize(&(self.secs, self.nanos), serializer) + } + } + + impl<'de> Deserialize<'de> for TimeDelta { + fn deserialize>(deserializer: D) -> Result { + let (secs, nanos) = <(i64, i32) as Deserialize>::deserialize(deserializer)?; + if !(MIN.secs..=MAX.secs).contains(&secs) + || nanos >= 1_000_000_000 + || (secs == MAX.secs && nanos > MAX.nanos) + || (secs == MIN.secs && nanos < MIN.nanos) + { + return Err(Error::custom("TimeDelta out of bounds")); + } + Ok(TimeDelta { secs, nanos }) + } + } + + #[cfg(test)] + mod tests { + use super::{TimeDelta, MAX}; + + #[test] + fn test_serde() { + let duration = TimeDelta::new(123, 456).unwrap(); + assert_eq!( + serde_json::from_value::(serde_json::to_value(duration).unwrap()) + .unwrap(), + duration + ); + } + + #[test] + #[should_panic(expected = "TimeDelta out of bounds")] + fn test_serde_oob_panic() { + let _ = + serde_json::from_value::(serde_json::json!([MAX.secs + 1, 0])).unwrap(); + } + } +} + #[cfg(test)] mod tests { use super::OutOfRangeError; @@ -1327,21 +1353,4 @@ mod tests { let bytes = rkyv::to_bytes::<_, 16>(&duration).unwrap(); assert_eq!(rkyv::from_bytes::(&bytes).unwrap(), duration); } - - #[test] - #[cfg(feature = "serde")] - fn test_serde() { - let duration = TimeDelta::new(123, 456).unwrap(); - assert_eq!( - serde_json::from_value::(serde_json::to_value(duration).unwrap()).unwrap(), - duration - ); - } - - #[test] - #[cfg(feature = "serde")] - #[should_panic(expected = "TimeDelta out of bounds")] - fn test_serde_oob_panic() { - let _ = serde_json::from_value::(serde_json::json!([MAX.secs + 1, 0])).unwrap(); - } } From 3c83d5817411ef822f0c5435923732300a16b4cf Mon Sep 17 00:00:00 2001 From: Awpteamoose Date: Mon, 16 Sep 2024 10:55:38 +0100 Subject: [PATCH 5/6] use TimeDelta::new instead of repeating checks --- src/time_delta.rs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/time_delta.rs b/src/time_delta.rs index 15c72b039..b9d0e1bd9 100644 --- a/src/time_delta.rs +++ b/src/time_delta.rs @@ -640,7 +640,7 @@ impl arbitrary::Arbitrary<'_> for TimeDelta { #[cfg(feature = "serde")] mod serde { - use super::{TimeDelta, MAX, MIN}; + use super::TimeDelta; use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer}; impl Serialize for TimeDelta { @@ -652,20 +652,13 @@ mod serde { impl<'de> Deserialize<'de> for TimeDelta { fn deserialize>(deserializer: D) -> Result { let (secs, nanos) = <(i64, i32) as Deserialize>::deserialize(deserializer)?; - if !(MIN.secs..=MAX.secs).contains(&secs) - || nanos >= 1_000_000_000 - || (secs == MAX.secs && nanos > MAX.nanos) - || (secs == MIN.secs && nanos < MIN.nanos) - { - return Err(Error::custom("TimeDelta out of bounds")); - } - Ok(TimeDelta { secs, nanos }) + TimeDelta::new(secs, nanos as u32).ok_or(Error::custom("TimeDelta out of bounds")) } } #[cfg(test)] mod tests { - use super::{TimeDelta, MAX}; + use super::{TimeDelta, super::MAX}; #[test] fn test_serde() { From 69ad2410d20fd51dd53b7b3cb6997fad72215e50 Mon Sep 17 00:00:00 2001 From: Awpteamoose Date: Mon, 16 Sep 2024 10:56:56 +0100 Subject: [PATCH 6/6] fmt --- src/time_delta.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/time_delta.rs b/src/time_delta.rs index b9d0e1bd9..d03068969 100644 --- a/src/time_delta.rs +++ b/src/time_delta.rs @@ -658,7 +658,7 @@ mod serde { #[cfg(test)] mod tests { - use super::{TimeDelta, super::MAX}; + use super::{super::MAX, TimeDelta}; #[test] fn test_serde() {