ruma_client_api/
profile.rs1use std::borrow::Cow;
4
5#[cfg(feature = "client")]
6use ruma_common::api::{
7 MatrixVersion,
8 path_builder::{StablePathSelector, VersionHistory},
9};
10use ruma_common::{OwnedMxcUri, serde::StringEnum};
11use serde::Serialize;
12use serde_json::{Value as JsonValue, from_value as from_json_value, to_value as to_json_value};
13
14pub mod delete_profile_field;
15pub mod get_avatar_url;
16pub mod get_display_name;
17pub mod get_profile;
18pub mod get_profile_field;
19mod profile_field_serde;
20pub mod set_avatar_url;
21pub mod set_display_name;
22pub mod set_profile_field;
23mod static_profile_field;
24
25pub use self::static_profile_field::*;
26
27#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
31#[derive(Clone, StringEnum)]
32#[ruma_enum(rename_all = "snake_case")]
33#[non_exhaustive]
34pub enum ProfileFieldName {
35 AvatarUrl,
37
38 #[ruma_enum(rename = "displayname")]
40 DisplayName,
41
42 #[ruma_enum(rename = "m.tz")]
44 TimeZone,
45
46 #[doc(hidden)]
47 _Custom(crate::PrivOwnedStr),
48}
49
50impl ProfileFieldName {
51 #[cfg(feature = "client")]
54 fn existed_before_extended_profiles(&self) -> bool {
55 matches!(self, Self::AvatarUrl | Self::DisplayName)
56 }
57}
58
59#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
63#[serde(rename_all = "snake_case")]
64#[non_exhaustive]
65pub enum ProfileFieldValue {
66 AvatarUrl(OwnedMxcUri),
68
69 #[serde(rename = "displayname")]
71 DisplayName(String),
72
73 #[serde(rename = "m.tz")]
75 TimeZone(String),
76
77 #[doc(hidden)]
78 #[serde(untagged)]
79 _Custom(CustomProfileFieldValue),
80}
81
82impl ProfileFieldValue {
83 pub fn new(field: &str, value: JsonValue) -> serde_json::Result<Self> {
94 Ok(match field {
95 "avatar_url" => Self::AvatarUrl(from_json_value(value)?),
96 "displayname" => Self::DisplayName(from_json_value(value)?),
97 "m.tz" => Self::TimeZone(from_json_value(value)?),
98 _ => Self::_Custom(CustomProfileFieldValue { field: field.to_owned(), value }),
99 })
100 }
101
102 pub fn field_name(&self) -> ProfileFieldName {
104 match self {
105 Self::AvatarUrl(_) => ProfileFieldName::AvatarUrl,
106 Self::DisplayName(_) => ProfileFieldName::DisplayName,
107 Self::TimeZone(_) => ProfileFieldName::TimeZone,
108 Self::_Custom(CustomProfileFieldValue { field, .. }) => field.as_str().into(),
109 }
110 }
111
112 pub fn value(&self) -> Cow<'_, JsonValue> {
117 match self {
118 Self::AvatarUrl(value) => {
119 Cow::Owned(to_json_value(value).expect("value should serialize successfully"))
120 }
121 Self::DisplayName(value) => {
122 Cow::Owned(to_json_value(value).expect("value should serialize successfully"))
123 }
124 Self::TimeZone(value) => {
125 Cow::Owned(to_json_value(value).expect("value should serialize successfully"))
126 }
127 Self::_Custom(c) => Cow::Borrowed(&c.value),
128 }
129 }
130}
131
132#[derive(Debug, Clone, PartialEq, Eq)]
134#[doc(hidden)]
135pub struct CustomProfileFieldValue {
136 field: String,
138
139 value: JsonValue,
141}
142
143#[cfg(feature = "client")]
145const EXTENDED_PROFILE_FIELD_HISTORY: VersionHistory = VersionHistory::new(
146 &[(
147 Some("uk.tcpip.msc4133"),
148 "/_matrix/client/unstable/uk.tcpip.msc4133/profile/{user_id}/{field}",
149 )],
150 &[(
151 StablePathSelector::Version(MatrixVersion::V1_16),
152 "/_matrix/client/v3/profile/{user_id}/{field}",
153 )],
154 None,
155 None,
156);
157
158#[cfg(test)]
159mod tests {
160 use ruma_common::owned_mxc_uri;
161 use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
162
163 use super::ProfileFieldValue;
164
165 #[test]
166 fn serialize_profile_field_value() {
167 let value = ProfileFieldValue::AvatarUrl(owned_mxc_uri!("mxc://localhost/abcdef"));
169 assert_eq!(
170 to_json_value(value).unwrap(),
171 json!({ "avatar_url": "mxc://localhost/abcdef" })
172 );
173
174 let value = ProfileFieldValue::DisplayName("Alice".to_owned());
176 assert_eq!(to_json_value(value).unwrap(), json!({ "displayname": "Alice" }));
177
178 let value = ProfileFieldValue::new("custom_field", "value".into()).unwrap();
180 assert_eq!(to_json_value(value).unwrap(), json!({ "custom_field": "value" }));
181 }
182
183 #[test]
184 fn deserialize_any_profile_field_value() {
185 let json = json!({ "avatar_url": "mxc://localhost/abcdef" });
187 assert_eq!(
188 from_json_value::<ProfileFieldValue>(json).unwrap(),
189 ProfileFieldValue::AvatarUrl(owned_mxc_uri!("mxc://localhost/abcdef"))
190 );
191
192 let json = json!({ "displayname": "Alice" });
194 assert_eq!(
195 from_json_value::<ProfileFieldValue>(json).unwrap(),
196 ProfileFieldValue::DisplayName("Alice".to_owned())
197 );
198
199 let json = json!({ "custom_field": "value" });
201 let value = from_json_value::<ProfileFieldValue>(json).unwrap();
202 assert_eq!(value.field_name().as_str(), "custom_field");
203 assert_eq!(value.value().as_str(), Some("value"));
204
205 let json = json!({});
207 from_json_value::<ProfileFieldValue>(json).unwrap_err();
208 }
209}