diff --git a/src/r0/contact/get_contacts.rs b/src/r0/contact/get_contacts.rs index a67b2994..f033ce10 100644 --- a/src/r0/contact/get_contacts.rs +++ b/src/r0/contact/get_contacts.rs @@ -1,9 +1,12 @@ -//! [GET /_matrix/client/r0/account/3pid](https://matrix.org/docs/spec/client_server/r0.4.0.html#get-matrix-client-r0-account-3pid) +//! [GET /_matrix/client/r0/account/3pid](https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-account-3pid) + +use std::time::SystemTime; -use crate::r0::thirdparty::Medium; use ruma_api::ruma_api; use serde::{Deserialize, Serialize}; +use crate::r0::thirdparty::Medium; + ruma_api! { metadata { description: "Get a list of 3rd party contacts associated with the user's account.", @@ -19,6 +22,7 @@ ruma_api! { response { /// A list of third party identifiers the homeserver has associated with the user's /// account. + #[serde(default, skip_serializing_if = "Vec::is_empty")] pub threepids: Vec, } @@ -27,9 +31,51 @@ ruma_api! { /// An identifier external to Matrix. #[derive(Clone, Debug, Deserialize, Serialize)] +#[cfg_attr(test, derive(PartialEq))] pub struct ThirdPartyIdentifier { /// The third party identifier address. pub address: String, /// The medium of third party identifier. pub medium: Medium, + /// The time when the identifier was validated by the identity server. + #[serde(with = "crate::serde::time::ms_since_unix_epoch")] + pub validated_at: SystemTime, + /// The time when the homeserver associated the third party identifier with the user. + #[serde(with = "crate::serde::time::ms_since_unix_epoch")] + pub added_at: SystemTime, +} + +#[cfg(test)] +mod tests { + use std::time::{Duration, UNIX_EPOCH}; + + use serde_json::{from_value as from_json_value, json, to_value as to_json_value}; + + use super::{Medium, ThirdPartyIdentifier}; + + #[test] + fn third_party_identifier_serde() { + let third_party_id = ThirdPartyIdentifier { + address: "monkey@banana.island".into(), + medium: Medium::Email, + validated_at: UNIX_EPOCH + Duration::from_millis(1535176800000), + added_at: UNIX_EPOCH + Duration::from_millis(1535336848756), + }; + + let third_party_id_serialized = json!({ + "medium": "email", + "address": "monkey@banana.island", + "validated_at": 1535176800000u64, + "added_at": 1535336848756u64 + }); + + assert_eq!( + to_json_value(third_party_id.clone()).unwrap(), + third_party_id_serialized.clone() + ); + assert_eq!( + third_party_id, + from_json_value(third_party_id_serialized).unwrap() + ); + } } diff --git a/src/serde.rs b/src/serde.rs index e89573d3..79c08e92 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -2,6 +2,7 @@ pub mod duration; pub mod json_string; +pub mod time; pub fn is_default(val: &T) -> bool { val == &T::default() diff --git a/src/serde/time.rs b/src/serde/time.rs new file mode 100644 index 00000000..8605814c --- /dev/null +++ b/src/serde/time.rs @@ -0,0 +1,3 @@ +//! De-/serialization functions for `std::time::SystemTime` objects + +pub mod ms_since_unix_epoch; diff --git a/src/serde/time/ms_since_unix_epoch.rs b/src/serde/time/ms_since_unix_epoch.rs new file mode 100644 index 00000000..ea1b79cc --- /dev/null +++ b/src/serde/time/ms_since_unix_epoch.rs @@ -0,0 +1,78 @@ +//! De-/serialization functions for `std::time::SystemTime` objects represented as milliseconds +//! since the UNIX epoch. Delegates to `js_int::UInt` to ensure integer size is within bounds. + +use std::{ + convert::TryFrom, + time::{Duration, SystemTime, UNIX_EPOCH}, +}; + +use js_int::UInt; +use serde::{ + de::{Deserialize, Deserializer}, + ser::{Error, Serialize, Serializer}, +}; + +/// Serialize a SystemTime. +/// +/// Will fail if integer is greater than the maximum integer that can be unambiguously represented +/// by an f64. +pub fn serialize(time: &SystemTime, serializer: S) -> Result +where + S: Serializer, +{ + // If this unwrap fails, the system this is executed is completely broken. + let time_since_epoch = time.duration_since(UNIX_EPOCH).unwrap(); + match UInt::try_from(time_since_epoch.as_millis()) { + Ok(uint) => uint.serialize(serializer), + Err(err) => Err(S::Error::custom(err)), + } +} + +/// Deserializes a SystemTime. +/// +/// 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>, +{ + let millis = UInt::deserialize(deserializer)?; + Ok(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")] + timestamp: SystemTime, + } + + #[test] + fn test_deserialize() { + let json = json!({ "timestamp": 3000 }); + + assert_eq!( + serde_json::from_value::(json).unwrap(), + SystemTimeTest { + timestamp: UNIX_EPOCH + Duration::from_millis(3000), + }, + ); + } + + #[test] + fn test_serialize() { + let request = SystemTimeTest { + timestamp: UNIX_EPOCH + Duration::new(2, 0), + }; + assert_eq!( + serde_json::to_value(&request).unwrap(), + json!({ "timestamp": 2000 }) + ); + } +}