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}