diff --git a/crates/ruma-common/src/serde/duration.rs b/crates/ruma-common/src/serde/duration.rs index 4f8de31b..13434340 100644 --- a/crates/ruma-common/src/serde/duration.rs +++ b/crates/ruma-common/src/serde/duration.rs @@ -1,5 +1,6 @@ //! De-/serialization functions for `std::time::Duration` objects +pub mod ms; pub mod opt_ms; pub mod opt_secs; pub mod secs; diff --git a/crates/ruma-common/src/serde/duration/ms.rs b/crates/ruma-common/src/serde/duration/ms.rs new file mode 100644 index 00000000..e0e87595 --- /dev/null +++ b/crates/ruma-common/src/serde/duration/ms.rs @@ -0,0 +1,67 @@ +//! De-/serialization functions for `Option` objects represented as +//! milliseconds. +//! +//! Delegates to `js_int::UInt` to ensure integer size is within bounds. + +use std::time::Duration; + +use js_int::UInt; +use serde::{ + de::{Deserialize, Deserializer}, + ser::{Error, Serialize, Serializer}, +}; + +/// Serializes a Duration to an integer representing seconds. +/// +/// Will fail if integer is greater than the maximum integer that can be +/// unambiguously represented by an f64. +pub fn serialize(duration: &Duration, serializer: S) -> Result +where + S: Serializer, +{ + match UInt::try_from(duration.as_millis()) { + Ok(uint) => uint.serialize(serializer), + Err(err) => Err(S::Error::custom(err)), + } +} + +/// Deserializes an integer representing seconds into a Duration. +/// +/// Will fail if integer is greater than the maximum integer that can be +/// unambiguously represented by an f64. +pub fn deserialize<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + UInt::deserialize(deserializer).map(|ms| Duration::from_millis(ms.into())) +} + +#[cfg(test)] +mod tests { + use std::time::Duration; + + use serde::{Deserialize, Serialize}; + use serde_json::json; + + #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] + struct DurationTest { + #[serde(with = "super")] + timeout: Duration, + } + + #[test] + fn deserialize() { + let json = json!({ "timeout": 3000 }); + + assert_eq!( + serde_json::from_value::(json).unwrap(), + DurationTest { timeout: Duration::from_secs(3) }, + ); + } + + #[test] + fn serialize() { + let test = DurationTest { timeout: Duration::from_millis(7000) }; + assert_eq!(serde_json::to_value(test).unwrap(), json!({ "timeout": 7000 })); + } +}