ruma_client_api/
profile.rs1#[cfg(feature = "unstable-msc4133")]
4use std::borrow::Cow;
5
6#[cfg(feature = "unstable-msc4133")]
7use ruma_common::{
8    serde::{OrdAsRefStr, PartialOrdAsRefStr, StringEnum},
9    OwnedMxcUri,
10};
11#[cfg(feature = "unstable-msc4133")]
12use serde::{de::DeserializeOwned, Serialize};
13#[cfg(feature = "unstable-msc4133")]
14use serde_json::{from_value as from_json_value, to_value as to_json_value, Value as JsonValue};
15
16#[cfg(feature = "unstable-msc4133")]
17pub mod delete_profile_field;
18pub mod get_avatar_url;
19pub mod get_display_name;
20pub mod get_profile;
21#[cfg(feature = "unstable-msc4133")]
22pub mod get_profile_field;
23#[cfg(feature = "unstable-msc4133")]
24mod profile_field_serde;
25pub mod set_avatar_url;
26pub mod set_display_name;
27#[cfg(feature = "unstable-msc4133")]
28pub mod set_profile_field;
29
30#[cfg(feature = "unstable-msc4133")]
33pub trait StaticProfileField {
34    type Value: Sized + Serialize + DeserializeOwned;
36
37    const NAME: &str;
39}
40
41#[derive(Debug, Clone, Copy)]
43#[cfg(feature = "unstable-msc4133")]
44#[allow(clippy::exhaustive_structs)]
45pub struct AvatarUrl;
46
47#[cfg(feature = "unstable-msc4133")]
48impl StaticProfileField for AvatarUrl {
49    type Value = OwnedMxcUri;
50    const NAME: &str = "avatar_url";
51}
52
53#[derive(Debug, Clone, Copy)]
55#[cfg(feature = "unstable-msc4133")]
56#[allow(clippy::exhaustive_structs)]
57pub struct DisplayName;
58
59#[cfg(feature = "unstable-msc4133")]
60impl StaticProfileField for DisplayName {
61    type Value = String;
62    const NAME: &str = "displayname";
63}
64
65#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
67#[cfg(feature = "unstable-msc4133")]
68#[derive(Clone, PartialEq, Eq, PartialOrdAsRefStr, OrdAsRefStr, StringEnum)]
69#[ruma_enum(rename_all = "snake_case")]
70#[non_exhaustive]
71pub enum ProfileFieldName {
72    AvatarUrl,
74
75    #[ruma_enum(rename = "displayname")]
77    DisplayName,
78
79    #[doc(hidden)]
80    _Custom(crate::PrivOwnedStr),
81}
82
83#[cfg(feature = "unstable-msc4133")]
85#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
86#[serde(rename_all = "snake_case")]
87#[non_exhaustive]
88pub enum ProfileFieldValue {
89    AvatarUrl(OwnedMxcUri),
91
92    #[serde(rename = "displayname")]
94    DisplayName(String),
95
96    #[doc(hidden)]
97    #[serde(untagged)]
98    _Custom(CustomProfileFieldValue),
99}
100
101#[cfg(feature = "unstable-msc4133")]
102impl ProfileFieldValue {
103    pub fn new(field: &str, value: JsonValue) -> serde_json::Result<Self> {
114        Ok(match field {
115            "avatar_url" => Self::AvatarUrl(from_json_value(value)?),
116            "displayname" => Self::DisplayName(from_json_value(value)?),
117            _ => Self::_Custom(CustomProfileFieldValue { field: field.to_owned(), value }),
118        })
119    }
120
121    pub fn field_name(&self) -> ProfileFieldName {
123        match self {
124            Self::AvatarUrl(_) => ProfileFieldName::AvatarUrl,
125            Self::DisplayName(_) => ProfileFieldName::DisplayName,
126            Self::_Custom(CustomProfileFieldValue { field, .. }) => field.as_str().into(),
127        }
128    }
129
130    pub fn value(&self) -> Cow<'_, JsonValue> {
135        match self {
136            Self::AvatarUrl(value) => {
137                Cow::Owned(to_json_value(value).expect("value should serialize successfully"))
138            }
139            Self::DisplayName(value) => {
140                Cow::Owned(to_json_value(value).expect("value should serialize successfully"))
141            }
142            Self::_Custom(c) => Cow::Borrowed(&c.value),
143        }
144    }
145}
146
147#[cfg(feature = "unstable-msc4133")]
149#[derive(Debug, Clone, PartialEq, Eq)]
150#[doc(hidden)]
151pub struct CustomProfileFieldValue {
152    field: String,
154
155    value: JsonValue,
157}
158
159#[cfg(all(test, feature = "unstable-msc4133"))]
160mod tests {
161    use ruma_common::owned_mxc_uri;
162    use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
163
164    use super::ProfileFieldValue;
165
166    #[test]
167    fn serialize_profile_field_value() {
168        let value = ProfileFieldValue::AvatarUrl(owned_mxc_uri!("mxc://localhost/abcdef"));
170        assert_eq!(
171            to_json_value(value).unwrap(),
172            json!({ "avatar_url": "mxc://localhost/abcdef" })
173        );
174
175        let value = ProfileFieldValue::DisplayName("Alice".to_owned());
177        assert_eq!(to_json_value(value).unwrap(), json!({ "displayname": "Alice" }));
178
179        let value = ProfileFieldValue::new("custom_field", "value".into()).unwrap();
181        assert_eq!(to_json_value(value).unwrap(), json!({ "custom_field": "value" }));
182    }
183
184    #[test]
185    fn deserialize_any_profile_field_value() {
186        let json = json!({ "avatar_url": "mxc://localhost/abcdef" });
188        assert_eq!(
189            from_json_value::<ProfileFieldValue>(json).unwrap(),
190            ProfileFieldValue::AvatarUrl(owned_mxc_uri!("mxc://localhost/abcdef"))
191        );
192
193        let json = json!({ "displayname": "Alice" });
195        assert_eq!(
196            from_json_value::<ProfileFieldValue>(json).unwrap(),
197            ProfileFieldValue::DisplayName("Alice".to_owned())
198        );
199
200        let json = json!({ "custom_field": "value" });
202        let value = from_json_value::<ProfileFieldValue>(json).unwrap();
203        assert_eq!(value.field_name().as_str(), "custom_field");
204        assert_eq!(value.value().as_str(), Some("value"));
205
206        let json = json!({});
208        from_json_value::<ProfileFieldValue>(json).unwrap_err();
209    }
210}