1use std::borrow::Cow;
4
5use ruma_macros::StringEnum;
6use serde::Serialize;
7use serde_json::{Value as JsonValue, from_value as from_json_value, to_value as to_json_value};
8
9use crate::{OwnedMxcUri, PrivOwnedStr};
10
11mod profile_field_value_serde;
12
13#[doc(hidden)]
14pub use self::profile_field_value_serde::ProfileFieldValueVisitor;
15
16#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
20#[derive(Clone, StringEnum)]
21#[ruma_enum(rename_all = "snake_case")]
22#[non_exhaustive]
23pub enum ProfileFieldName {
24 AvatarUrl,
26
27 #[ruma_enum(rename = "displayname")]
29 DisplayName,
30
31 #[ruma_enum(rename = "m.tz")]
33 TimeZone,
34
35 #[doc(hidden)]
36 _Custom(PrivOwnedStr),
37}
38
39#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
43#[serde(rename_all = "snake_case")]
44#[non_exhaustive]
45pub enum ProfileFieldValue {
46 AvatarUrl(OwnedMxcUri),
48
49 #[serde(rename = "displayname")]
51 DisplayName(String),
52
53 #[serde(rename = "m.tz")]
55 TimeZone(String),
56
57 #[doc(hidden)]
58 #[serde(untagged)]
59 _Custom(CustomProfileFieldValue),
60}
61
62impl ProfileFieldValue {
63 pub fn new(field: &str, value: JsonValue) -> serde_json::Result<Self> {
74 Ok(match field {
75 "avatar_url" => Self::AvatarUrl(from_json_value(value)?),
76 "displayname" => Self::DisplayName(from_json_value(value)?),
77 "m.tz" => Self::TimeZone(from_json_value(value)?),
78 _ => Self::_Custom(CustomProfileFieldValue { field: field.to_owned(), value }),
79 })
80 }
81
82 pub fn field_name(&self) -> ProfileFieldName {
84 match self {
85 Self::AvatarUrl(_) => ProfileFieldName::AvatarUrl,
86 Self::DisplayName(_) => ProfileFieldName::DisplayName,
87 Self::TimeZone(_) => ProfileFieldName::TimeZone,
88 Self::_Custom(CustomProfileFieldValue { field, .. }) => field.as_str().into(),
89 }
90 }
91
92 pub fn value(&self) -> Cow<'_, JsonValue> {
97 match self {
98 Self::AvatarUrl(value) => {
99 Cow::Owned(to_json_value(value).expect("value should serialize successfully"))
100 }
101 Self::DisplayName(value) => {
102 Cow::Owned(to_json_value(value).expect("value should serialize successfully"))
103 }
104 Self::TimeZone(value) => {
105 Cow::Owned(to_json_value(value).expect("value should serialize successfully"))
106 }
107 Self::_Custom(c) => Cow::Borrowed(&c.value),
108 }
109 }
110}
111
112#[derive(Debug, Clone, PartialEq, Eq)]
114#[doc(hidden)]
115pub struct CustomProfileFieldValue {
116 field: String,
118
119 value: JsonValue,
121}
122
123#[cfg(test)]
124mod tests {
125 use ruma_common::{canonical_json::assert_to_canonical_json_eq, owned_mxc_uri};
126 use serde_json::{from_value as from_json_value, json};
127
128 use super::ProfileFieldValue;
129
130 #[test]
131 fn serialize_profile_field_value() {
132 let value = ProfileFieldValue::AvatarUrl(owned_mxc_uri!("mxc://localhost/abcdef"));
134 assert_to_canonical_json_eq!(value, json!({ "avatar_url": "mxc://localhost/abcdef" }));
135
136 let value = ProfileFieldValue::DisplayName("Alice".to_owned());
138 assert_to_canonical_json_eq!(value, json!({ "displayname": "Alice" }));
139
140 let value = ProfileFieldValue::new("custom_field", "value".into()).unwrap();
142 assert_to_canonical_json_eq!(value, json!({ "custom_field": "value" }));
143 }
144
145 #[test]
146 fn deserialize_profile_field_value() {
147 let json = json!({ "avatar_url": "mxc://localhost/abcdef" });
149 assert_eq!(
150 from_json_value::<ProfileFieldValue>(json).unwrap(),
151 ProfileFieldValue::AvatarUrl(owned_mxc_uri!("mxc://localhost/abcdef"))
152 );
153
154 let json = json!({ "displayname": "Alice" });
156 assert_eq!(
157 from_json_value::<ProfileFieldValue>(json).unwrap(),
158 ProfileFieldValue::DisplayName("Alice".to_owned())
159 );
160
161 let json = json!({ "custom_field": "value" });
163 let value = from_json_value::<ProfileFieldValue>(json).unwrap();
164 assert_eq!(value.field_name().as_str(), "custom_field");
165 assert_eq!(value.value().as_str(), Some("value"));
166
167 let json = json!({});
169 from_json_value::<ProfileFieldValue>(json).unwrap_err();
170 }
171}