ruma_client_api/discovery/
get_capabilities.rs
1use std::{borrow::Cow, collections::BTreeMap};
9
10use maplit::btreemap;
11use ruma_common::{serde::StringEnum, RoomVersionId};
12use serde::{Deserialize, Serialize};
13use serde_json::{from_value as from_json_value, to_value as to_json_value, Value as JsonValue};
14
15use self::iter::{CapabilitiesIter, CapabilityRef};
16use crate::PrivOwnedStr;
17
18pub mod iter;
19pub mod v3;
20
21#[derive(Clone, Debug, Default, Serialize, Deserialize)]
23#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
24pub struct Capabilities {
25 #[serde(
27 rename = "m.change_password",
28 default,
29 skip_serializing_if = "ChangePasswordCapability::is_default"
30 )]
31 pub change_password: ChangePasswordCapability,
32
33 #[serde(
35 rename = "m.room_versions",
36 default,
37 skip_serializing_if = "RoomVersionsCapability::is_default"
38 )]
39 pub room_versions: RoomVersionsCapability,
40
41 #[serde(
43 rename = "m.set_displayname",
44 default,
45 skip_serializing_if = "SetDisplayNameCapability::is_default"
46 )]
47 pub set_displayname: SetDisplayNameCapability,
48
49 #[serde(
51 rename = "m.set_avatar_url",
52 default,
53 skip_serializing_if = "SetAvatarUrlCapability::is_default"
54 )]
55 pub set_avatar_url: SetAvatarUrlCapability,
56
57 #[serde(
60 rename = "m.3pid_changes",
61 default,
62 skip_serializing_if = "ThirdPartyIdChangesCapability::is_default"
63 )]
64 pub thirdparty_id_changes: ThirdPartyIdChangesCapability,
65
66 #[serde(
69 rename = "m.get_login_token",
70 default,
71 skip_serializing_if = "GetLoginTokenCapability::is_default"
72 )]
73 pub get_login_token: GetLoginTokenCapability,
74
75 #[serde(flatten)]
78 custom_capabilities: BTreeMap<String, JsonValue>,
79}
80
81impl Capabilities {
82 pub fn new() -> Self {
84 Default::default()
85 }
86
87 pub fn get(&self, capability: &str) -> Option<Cow<'_, JsonValue>> {
92 fn serialize<T: Serialize>(cap: &T) -> JsonValue {
93 to_json_value(cap).expect("capability serialization to succeed")
94 }
95
96 match capability {
97 "m.change_password" => Some(Cow::Owned(serialize(&self.change_password))),
98 "m.room_versions" => Some(Cow::Owned(serialize(&self.room_versions))),
99 "m.set_displayname" => Some(Cow::Owned(serialize(&self.set_displayname))),
100 "m.set_avatar_url" => Some(Cow::Owned(serialize(&self.set_avatar_url))),
101 "m.3pid_changes" => Some(Cow::Owned(serialize(&self.thirdparty_id_changes))),
102 _ => self.custom_capabilities.get(capability).map(Cow::Borrowed),
103 }
104 }
105
106 pub fn set(&mut self, capability: &str, value: JsonValue) -> serde_json::Result<()> {
112 match capability {
113 "m.change_password" => self.change_password = from_json_value(value)?,
114 "m.room_versions" => self.room_versions = from_json_value(value)?,
115 "m.set_displayname" => self.set_displayname = from_json_value(value)?,
116 "m.set_avatar_url" => self.set_avatar_url = from_json_value(value)?,
117 "m.3pid_changes" => self.thirdparty_id_changes = from_json_value(value)?,
118 _ => {
119 self.custom_capabilities.insert(capability.to_owned(), value);
120 }
121 }
122
123 Ok(())
124 }
125
126 pub fn iter(&self) -> CapabilitiesIter<'_> {
128 CapabilitiesIter::new(self)
129 }
130}
131
132impl<'a> IntoIterator for &'a Capabilities {
133 type Item = CapabilityRef<'a>;
134 type IntoIter = CapabilitiesIter<'a>;
135
136 fn into_iter(self) -> Self::IntoIter {
137 self.iter()
138 }
139}
140
141#[derive(Clone, Debug, Serialize, Deserialize)]
143#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
144pub struct ChangePasswordCapability {
145 pub enabled: bool,
147}
148
149impl ChangePasswordCapability {
150 pub fn new(enabled: bool) -> Self {
152 Self { enabled }
153 }
154
155 pub fn is_default(&self) -> bool {
157 self.enabled
158 }
159}
160
161impl Default for ChangePasswordCapability {
162 fn default() -> Self {
163 Self { enabled: true }
164 }
165}
166
167#[derive(Clone, Debug, Serialize, Deserialize)]
169#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
170pub struct RoomVersionsCapability {
171 pub default: RoomVersionId,
173
174 pub available: BTreeMap<RoomVersionId, RoomVersionStability>,
176}
177
178impl RoomVersionsCapability {
179 pub fn new(
182 default: RoomVersionId,
183 available: BTreeMap<RoomVersionId, RoomVersionStability>,
184 ) -> Self {
185 Self { default, available }
186 }
187
188 pub fn is_default(&self) -> bool {
190 self.default == RoomVersionId::V1
191 && self.available.len() == 1
192 && self
193 .available
194 .get(&RoomVersionId::V1)
195 .map(|stability| *stability == RoomVersionStability::Stable)
196 .unwrap_or(false)
197 }
198}
199
200impl Default for RoomVersionsCapability {
201 fn default() -> Self {
202 Self {
203 default: RoomVersionId::V1,
204 available: btreemap! { RoomVersionId::V1 => RoomVersionStability::Stable },
205 }
206 }
207}
208
209#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
211#[derive(Clone, PartialEq, Eq, StringEnum)]
212#[ruma_enum(rename_all = "lowercase")]
213#[non_exhaustive]
214pub enum RoomVersionStability {
215 Stable,
217
218 Unstable,
220
221 #[doc(hidden)]
222 _Custom(PrivOwnedStr),
223}
224
225#[derive(Clone, Debug, Serialize, Deserialize)]
227#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
228pub struct SetDisplayNameCapability {
229 pub enabled: bool,
231}
232
233impl SetDisplayNameCapability {
234 pub fn new(enabled: bool) -> Self {
236 Self { enabled }
237 }
238
239 pub fn is_default(&self) -> bool {
241 self.enabled
242 }
243}
244
245impl Default for SetDisplayNameCapability {
246 fn default() -> Self {
247 Self { enabled: true }
248 }
249}
250
251#[derive(Clone, Debug, Serialize, Deserialize)]
253#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
254pub struct SetAvatarUrlCapability {
255 pub enabled: bool,
257}
258
259impl SetAvatarUrlCapability {
260 pub fn new(enabled: bool) -> Self {
262 Self { enabled }
263 }
264
265 pub fn is_default(&self) -> bool {
267 self.enabled
268 }
269}
270
271impl Default for SetAvatarUrlCapability {
272 fn default() -> Self {
273 Self { enabled: true }
274 }
275}
276
277#[derive(Clone, Debug, Serialize, Deserialize)]
279#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
280pub struct ThirdPartyIdChangesCapability {
281 pub enabled: bool,
284}
285
286impl ThirdPartyIdChangesCapability {
287 pub fn new(enabled: bool) -> Self {
289 Self { enabled }
290 }
291
292 pub fn is_default(&self) -> bool {
294 self.enabled
295 }
296}
297
298impl Default for ThirdPartyIdChangesCapability {
299 fn default() -> Self {
300 Self { enabled: true }
301 }
302}
303
304#[derive(Clone, Debug, Default, Serialize, Deserialize)]
306#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
307pub struct GetLoginTokenCapability {
308 pub enabled: bool,
310}
311
312impl GetLoginTokenCapability {
313 pub fn new(enabled: bool) -> Self {
315 Self { enabled }
316 }
317
318 pub fn is_default(&self) -> bool {
320 !self.enabled
321 }
322}
323
324#[cfg(test)]
325mod tests {
326 use std::borrow::Cow;
327
328 use assert_matches2::assert_matches;
329 use serde_json::json;
330
331 use super::Capabilities;
332
333 #[test]
334 fn capabilities_iter() -> serde_json::Result<()> {
335 let mut caps = Capabilities::new();
336 let custom_cap = json!({
337 "key": "value",
338 });
339 caps.set("m.some_random_capability", custom_cap)?;
340 let mut caps_iter = caps.iter();
341
342 let iter_res = caps_iter.next().unwrap();
343 assert_eq!(iter_res.name(), "m.change_password");
344 assert_eq!(iter_res.value(), Cow::Borrowed(&json!({ "enabled": true })));
345
346 let iter_res = caps_iter.next().unwrap();
347 assert_eq!(iter_res.name(), "m.room_versions");
348 assert_eq!(
349 iter_res.value(),
350 Cow::Borrowed(&json!({ "available": { "1": "stable" },"default" :"1" }))
351 );
352
353 let iter_res = caps_iter.next().unwrap();
354 assert_eq!(iter_res.name(), "m.set_displayname");
355 assert_eq!(iter_res.value(), Cow::Borrowed(&json!({ "enabled": true })));
356
357 let iter_res = caps_iter.next().unwrap();
358 assert_eq!(iter_res.name(), "m.set_avatar_url");
359 assert_eq!(iter_res.value(), Cow::Borrowed(&json!({ "enabled": true })));
360
361 let iter_res = caps_iter.next().unwrap();
362 assert_eq!(iter_res.name(), "m.3pid_changes");
363 assert_eq!(iter_res.value(), Cow::Borrowed(&json!({ "enabled": true })));
364
365 let iter_res = caps_iter.next().unwrap();
366 assert_eq!(iter_res.name(), "m.some_random_capability");
367 assert_eq!(iter_res.value(), Cow::Borrowed(&json!({ "key": "value" })));
368
369 assert_matches!(caps_iter.next(), None);
370 Ok(())
371 }
372}