//! Helper module for the Serialize / Deserialize impl's for the UserIdentifier struct //! in the parent module. use ruma_common::{serde::from_raw_json_value, thirdparty::Medium}; use serde::{de, ser::SerializeStruct, Deserialize, Deserializer, Serialize}; use serde_json::value::RawValue as RawJsonValue; use super::{ CustomThirdPartyId, IncomingCustomThirdPartyId, IncomingUserIdentifier, UserIdentifier, }; impl<'a> Serialize for UserIdentifier<'a> { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { let mut id; match self { Self::UserIdOrLocalpart(user) => { id = serializer.serialize_struct("UserIdentifier", 2)?; id.serialize_field("type", "m.id.user")?; id.serialize_field("user", user)?; } Self::PhoneNumber { country, phone } => { id = serializer.serialize_struct("UserIdentifier", 3)?; id.serialize_field("type", "m.id.phone")?; id.serialize_field("country", country)?; id.serialize_field("phone", phone)?; } Self::Email { address } => { id = serializer.serialize_struct("UserIdentifier", 3)?; id.serialize_field("type", "m.id.thirdparty")?; id.serialize_field("medium", &Medium::Email)?; id.serialize_field("address", address)?; } Self::Msisdn { number } => { id = serializer.serialize_struct("UserIdentifier", 3)?; id.serialize_field("type", "m.id.thirdparty")?; id.serialize_field("medium", &Medium::Msisdn)?; id.serialize_field("address", number)?; } Self::_CustomThirdParty(CustomThirdPartyId { medium, address }) => { id = serializer.serialize_struct("UserIdentifier", 3)?; id.serialize_field("type", "m.id.thirdparty")?; id.serialize_field("medium", &medium)?; id.serialize_field("address", address)?; } } id.end() } } impl<'de> Deserialize<'de> for IncomingUserIdentifier { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { let json = Box::::deserialize(deserializer)?; #[derive(Deserialize)] #[serde(tag = "type")] enum ExtractType { #[serde(rename = "m.id.user")] User, #[serde(rename = "m.id.phone")] Phone, #[serde(rename = "m.id.thirdparty")] ThirdParty, } #[derive(Deserialize)] struct UserIdOrLocalpart { user: String, } #[derive(Deserialize)] struct ThirdPartyId { medium: Medium, address: String, } #[derive(Deserialize)] struct PhoneNumber { country: String, phone: String, } let id_type = serde_json::from_str::(json.get()).map_err(de::Error::custom)?; match id_type { ExtractType::User => from_raw_json_value(&json) .map(|user_id: UserIdOrLocalpart| Self::UserIdOrLocalpart(user_id.user)), ExtractType::Phone => from_raw_json_value(&json) .map(|nb: PhoneNumber| Self::PhoneNumber { country: nb.country, phone: nb.phone }), ExtractType::ThirdParty => { let ThirdPartyId { medium, address } = from_raw_json_value(&json)?; match medium { Medium::Email => Ok(Self::Email { address }), Medium::Msisdn => Ok(Self::Msisdn { number: address }), _ => { Ok(Self::_CustomThirdParty(IncomingCustomThirdPartyId { medium, address })) } } } } } } #[cfg(test)] mod tests { use crate::uiaa::{IncomingUserIdentifier, UserIdentifier}; use assert_matches::assert_matches; use serde_json::{from_value as from_json_value, json, to_value as to_json_value}; #[test] fn serialize() { assert_eq!( to_json_value(UserIdentifier::UserIdOrLocalpart("@user:notareal.hs")).unwrap(), json!({ "type": "m.id.user", "user": "@user:notareal.hs", }) ); assert_eq!( to_json_value(UserIdentifier::PhoneNumber { country: "33", phone: "0102030405" }) .unwrap(), json!({ "type": "m.id.phone", "country": "33", "phone": "0102030405", }) ); assert_eq!( to_json_value(UserIdentifier::Email { address: "me@myprovider.net" }).unwrap(), json!({ "type": "m.id.thirdparty", "medium": "email", "address": "me@myprovider.net", }) ); assert_eq!( to_json_value(UserIdentifier::Msisdn { number: "330102030405" }).unwrap(), json!({ "type": "m.id.thirdparty", "medium": "msisdn", "address": "330102030405", }) ); assert_eq!( to_json_value(UserIdentifier::third_party_id(&"robot".into(), "01001110")).unwrap(), json!({ "type": "m.id.thirdparty", "medium": "robot", "address": "01001110", }) ); } #[test] fn deserialize() { let json = json!({ "type": "m.id.user", "user": "@user:notareal.hs", }); let user = assert_matches!( from_json_value(json), Ok(IncomingUserIdentifier::UserIdOrLocalpart(user)) => user ); assert_eq!(user, "@user:notareal.hs"); let json = json!({ "type": "m.id.phone", "country": "33", "phone": "0102030405", }); let (country, phone) = assert_matches!( from_json_value(json), Ok(IncomingUserIdentifier::PhoneNumber { country, phone }) => (country, phone) ); assert_eq!(country, "33"); assert_eq!(phone, "0102030405"); let json = json!({ "type": "m.id.thirdparty", "medium": "email", "address": "me@myprovider.net", }); let address = assert_matches!( from_json_value(json), Ok(IncomingUserIdentifier::Email { address }) => address ); assert_eq!(address, "me@myprovider.net"); let json = json!({ "type": "m.id.thirdparty", "medium": "msisdn", "address": "330102030405", }); let number = assert_matches!( from_json_value(json), Ok(IncomingUserIdentifier::Msisdn { number }) => number ); assert_eq!(number, "330102030405"); let json = json!({ "type": "m.id.thirdparty", "medium": "robot", "address": "01110010", }); let id = from_json_value::(json).unwrap(); let (medium, address) = id.to_outgoing().as_third_party_id().unwrap(); assert_eq!(medium.as_str(), "robot"); assert_eq!(address, "01110010"); } }