From 6ddb9eca8b5e06e72a4453c383262e93feaf19c2 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Thu, 16 Apr 2020 00:28:09 +0200 Subject: [PATCH] Add serde::opt_ms_since_unix_epoch, use it in device::Device --- CHANGELOG.md | 7 +- src/r0/device.rs | 10 ++- src/serde/time.rs | 1 + src/serde/time/opt_ms_since_unix_epoch.rs | 100 ++++++++++++++++++++++ 4 files changed, 113 insertions(+), 5 deletions(-) create mode 100644 src/serde/time/opt_ms_since_unix_epoch.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index fa449ab0..4c9ce527 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,9 +12,10 @@ Breaking changes: * Remove deprecated `home_server` response field (removed in r0.4.0) * Update `r0::contact::get_contacts` endpoint to r0.6.0 * Change `UInt` timestamps to `SystemTime` in: - * `media::get_media_preview` - * `push::get_notifications` - * `server::get_user_info` + * `media::get_media_preview::Request` + * `push::get_notifications::Notification` + * `server::get_user_info::ConnectionInfo` + * `device::Device` Improvements: diff --git a/src/r0/device.rs b/src/r0/device.rs index 11c27344..39523f2e 100644 --- a/src/r0/device.rs +++ b/src/r0/device.rs @@ -1,6 +1,7 @@ //! Endpoints for managing devices. -use js_int::UInt; +use std::time::SystemTime; + use ruma_identifiers::DeviceId; use serde::{Deserialize, Serialize}; @@ -20,5 +21,10 @@ pub struct Device { /// Most recently seen IP address of the session. pub ip: Option, /// Unix timestamp that the session was last active. - pub last_seen: Option, + #[serde( + with = "crate::serde::time::opt_ms_since_unix_epoch", + default, + skip_serializing_if = "Option::is_none" + )] + pub last_seen: Option, } diff --git a/src/serde/time.rs b/src/serde/time.rs index 8605814c..e60bb839 100644 --- a/src/serde/time.rs +++ b/src/serde/time.rs @@ -1,3 +1,4 @@ //! De-/serialization functions for `std::time::SystemTime` objects pub mod ms_since_unix_epoch; +pub mod opt_ms_since_unix_epoch; diff --git a/src/serde/time/opt_ms_since_unix_epoch.rs b/src/serde/time/opt_ms_since_unix_epoch.rs new file mode 100644 index 00000000..787455ac --- /dev/null +++ b/src/serde/time/opt_ms_since_unix_epoch.rs @@ -0,0 +1,100 @@ +//! De-/serialization functions for `Option` objects represented as +//! milliseconds since the UNIX epoch. Delegates to `js_int::UInt` to ensure integer size is within +//! bounds. + +use std::time::{Duration, SystemTime, UNIX_EPOCH}; + +use js_int::UInt; +use serde::{ + de::{Deserialize, Deserializer}, + ser::{Serialize, Serializer}, +}; + +/// Serialize an `Option`. +/// +/// Will fail if integer is greater than the maximum integer that can be unambiguously represented +/// by an f64. +pub fn serialize(opt_time: &Option, serializer: S) -> Result +where + S: Serializer, +{ + match opt_time { + Some(time) => super::ms_since_unix_epoch::serialize(time, serializer), + None => Option::::serialize(&None, serializer), + } +} + +/// Deserializes an `Option`. +/// +/// 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, D::Error> +where + D: Deserializer<'de>, +{ + Ok(Option::::deserialize(deserializer)? + .map(|millis| UNIX_EPOCH + Duration::from_millis(millis.into()))) +} + +#[cfg(test)] +mod tests { + use std::time::{Duration, SystemTime, UNIX_EPOCH}; + + use serde::{Deserialize, Serialize}; + use serde_json::json; + + #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] + struct SystemTimeTest { + #[serde(with = "super", default, skip_serializing_if = "Option::is_none")] + timestamp: Option, + } + + #[test] + fn test_deserialize_some() { + let json = json!({ "timestamp": 3000 }); + + assert_eq!( + serde_json::from_value::(json).unwrap(), + SystemTimeTest { + timestamp: Some(UNIX_EPOCH + Duration::from_millis(3000)) + }, + ); + } + + #[test] + fn test_deserialize_none_by_absence() { + let json = json!({}); + + assert_eq!( + serde_json::from_value::(json).unwrap(), + SystemTimeTest { timestamp: None }, + ); + } + + #[test] + fn test_deserialize_none_by_null() { + let json = json!({ "timestamp": null }); + + assert_eq!( + serde_json::from_value::(json).unwrap(), + SystemTimeTest { timestamp: None }, + ); + } + + #[test] + fn test_serialize_some() { + let request = SystemTimeTest { + timestamp: Some(UNIX_EPOCH + Duration::new(2, 0)), + }; + assert_eq!( + serde_json::to_value(&request).unwrap(), + json!({ "timestamp": 2000 }) + ); + } + + #[test] + fn test_serialize_none() { + let request = SystemTimeTest { timestamp: None }; + assert_eq!(serde_json::to_value(&request).unwrap(), json!({})); + } +}