1use ruma_common::{serde::from_raw_json_value, thirdparty::Medium};
5use serde::{de, ser::SerializeStruct, Deserialize, Deserializer, Serialize};
6use serde_json::value::RawValue as RawJsonValue;
7
8use super::{CustomThirdPartyId, UserIdentifier};
9
10impl Serialize for UserIdentifier {
11 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
12 where
13 S: serde::Serializer,
14 {
15 let mut id;
16 match self {
17 Self::UserIdOrLocalpart(user) => {
18 id = serializer.serialize_struct("UserIdentifier", 2)?;
19 id.serialize_field("type", "m.id.user")?;
20 id.serialize_field("user", user)?;
21 }
22 Self::PhoneNumber { country, phone } => {
23 id = serializer.serialize_struct("UserIdentifier", 3)?;
24 id.serialize_field("type", "m.id.phone")?;
25 id.serialize_field("country", country)?;
26 id.serialize_field("phone", phone)?;
27 }
28 Self::Email { address } => {
29 id = serializer.serialize_struct("UserIdentifier", 3)?;
30 id.serialize_field("type", "m.id.thirdparty")?;
31 id.serialize_field("medium", &Medium::Email)?;
32 id.serialize_field("address", address)?;
33 }
34 Self::Msisdn { number } => {
35 id = serializer.serialize_struct("UserIdentifier", 3)?;
36 id.serialize_field("type", "m.id.thirdparty")?;
37 id.serialize_field("medium", &Medium::Msisdn)?;
38 id.serialize_field("address", number)?;
39 }
40 Self::_CustomThirdParty(CustomThirdPartyId { medium, address }) => {
41 id = serializer.serialize_struct("UserIdentifier", 3)?;
42 id.serialize_field("type", "m.id.thirdparty")?;
43 id.serialize_field("medium", &medium)?;
44 id.serialize_field("address", address)?;
45 }
46 }
47 id.end()
48 }
49}
50
51impl<'de> Deserialize<'de> for UserIdentifier {
52 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
53 where
54 D: Deserializer<'de>,
55 {
56 let json = Box::<RawJsonValue>::deserialize(deserializer)?;
57
58 #[derive(Deserialize)]
59 #[serde(tag = "type")]
60 enum ExtractType {
61 #[serde(rename = "m.id.user")]
62 User,
63 #[serde(rename = "m.id.phone")]
64 Phone,
65 #[serde(rename = "m.id.thirdparty")]
66 ThirdParty,
67 }
68
69 #[derive(Deserialize)]
70 struct UserIdOrLocalpart {
71 user: String,
72 }
73
74 #[derive(Deserialize)]
75 struct ThirdPartyId {
76 medium: Medium,
77 address: String,
78 }
79
80 #[derive(Deserialize)]
81 struct PhoneNumber {
82 country: String,
83 phone: String,
84 }
85
86 let id_type = serde_json::from_str::<ExtractType>(json.get()).map_err(de::Error::custom)?;
87
88 match id_type {
89 ExtractType::User => from_raw_json_value(&json)
90 .map(|user_id: UserIdOrLocalpart| Self::UserIdOrLocalpart(user_id.user)),
91 ExtractType::Phone => from_raw_json_value(&json)
92 .map(|nb: PhoneNumber| Self::PhoneNumber { country: nb.country, phone: nb.phone }),
93 ExtractType::ThirdParty => {
94 let ThirdPartyId { medium, address } = from_raw_json_value(&json)?;
95 match medium {
96 Medium::Email => Ok(Self::Email { address }),
97 Medium::Msisdn => Ok(Self::Msisdn { number: address }),
98 _ => Ok(Self::_CustomThirdParty(CustomThirdPartyId { medium, address })),
99 }
100 }
101 }
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use assert_matches2::assert_matches;
108 use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
109
110 use crate::uiaa::UserIdentifier;
111
112 #[test]
113 fn serialize() {
114 assert_eq!(
115 to_json_value(UserIdentifier::UserIdOrLocalpart("@user:notareal.hs".to_owned()))
116 .unwrap(),
117 json!({
118 "type": "m.id.user",
119 "user": "@user:notareal.hs",
120 })
121 );
122
123 assert_eq!(
124 to_json_value(UserIdentifier::PhoneNumber {
125 country: "33".to_owned(),
126 phone: "0102030405".to_owned()
127 })
128 .unwrap(),
129 json!({
130 "type": "m.id.phone",
131 "country": "33",
132 "phone": "0102030405",
133 })
134 );
135
136 assert_eq!(
137 to_json_value(UserIdentifier::Email { address: "me@myprovider.net".to_owned() })
138 .unwrap(),
139 json!({
140 "type": "m.id.thirdparty",
141 "medium": "email",
142 "address": "me@myprovider.net",
143 })
144 );
145
146 assert_eq!(
147 to_json_value(UserIdentifier::Msisdn { number: "330102030405".to_owned() }).unwrap(),
148 json!({
149 "type": "m.id.thirdparty",
150 "medium": "msisdn",
151 "address": "330102030405",
152 })
153 );
154
155 assert_eq!(
156 to_json_value(UserIdentifier::third_party_id("robot".into(), "01001110".to_owned()))
157 .unwrap(),
158 json!({
159 "type": "m.id.thirdparty",
160 "medium": "robot",
161 "address": "01001110",
162 })
163 );
164 }
165
166 #[test]
167 fn deserialize() {
168 let json = json!({
169 "type": "m.id.user",
170 "user": "@user:notareal.hs",
171 });
172 assert_matches!(from_json_value(json), Ok(UserIdentifier::UserIdOrLocalpart(user)));
173 assert_eq!(user, "@user:notareal.hs");
174
175 let json = json!({
176 "type": "m.id.phone",
177 "country": "33",
178 "phone": "0102030405",
179 });
180 assert_matches!(from_json_value(json), Ok(UserIdentifier::PhoneNumber { country, phone }));
181 assert_eq!(country, "33");
182 assert_eq!(phone, "0102030405");
183
184 let json = json!({
185 "type": "m.id.thirdparty",
186 "medium": "email",
187 "address": "me@myprovider.net",
188 });
189 assert_matches!(from_json_value(json), Ok(UserIdentifier::Email { address }));
190 assert_eq!(address, "me@myprovider.net");
191
192 let json = json!({
193 "type": "m.id.thirdparty",
194 "medium": "msisdn",
195 "address": "330102030405",
196 });
197 assert_matches!(from_json_value(json), Ok(UserIdentifier::Msisdn { number }));
198 assert_eq!(number, "330102030405");
199
200 let json = json!({
201 "type": "m.id.thirdparty",
202 "medium": "robot",
203 "address": "01110010",
204 });
205 let id = from_json_value::<UserIdentifier>(json).unwrap();
206 let (medium, address) = id.as_third_party_id().unwrap();
207 assert_eq!(medium.as_str(), "robot");
208 assert_eq!(address, "01110010");
209 }
210}