ruma_client_api/uiaa/
user_serde.rs

1//! Helper module for the Serialize / Deserialize impl's for the UserIdentifier struct
2//! in the parent module.
3
4use 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}