ruma_client_api/discovery/
get_capabilities.rs1use 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 "m.get_login_token" => Some(Cow::Owned(serialize(&self.get_login_token))),
103 _ => self.custom_capabilities.get(capability).map(Cow::Borrowed),
104 }
105 }
106
107 pub fn set(&mut self, capability: &str, value: JsonValue) -> serde_json::Result<()> {
113 match capability {
114 "m.change_password" => self.change_password = from_json_value(value)?,
115 "m.room_versions" => self.room_versions = from_json_value(value)?,
116 "m.set_displayname" => self.set_displayname = from_json_value(value)?,
117 "m.set_avatar_url" => self.set_avatar_url = from_json_value(value)?,
118 "m.3pid_changes" => self.thirdparty_id_changes = from_json_value(value)?,
119 "m.get_login_token" => self.get_login_token = from_json_value(value)?,
120 _ => {
121 self.custom_capabilities.insert(capability.to_owned(), value);
122 }
123 }
124
125 Ok(())
126 }
127
128 pub fn iter(&self) -> CapabilitiesIter<'_> {
130 CapabilitiesIter::new(self)
131 }
132}
133
134impl<'a> IntoIterator for &'a Capabilities {
135 type Item = CapabilityRef<'a>;
136 type IntoIter = CapabilitiesIter<'a>;
137
138 fn into_iter(self) -> Self::IntoIter {
139 self.iter()
140 }
141}
142
143#[derive(Clone, Debug, Serialize, Deserialize)]
145#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
146pub struct ChangePasswordCapability {
147 pub enabled: bool,
149}
150
151impl ChangePasswordCapability {
152 pub fn new(enabled: bool) -> Self {
154 Self { enabled }
155 }
156
157 pub fn is_default(&self) -> bool {
159 self.enabled
160 }
161}
162
163impl Default for ChangePasswordCapability {
164 fn default() -> Self {
165 Self { enabled: true }
166 }
167}
168
169#[derive(Clone, Debug, Serialize, Deserialize)]
171#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
172pub struct RoomVersionsCapability {
173 pub default: RoomVersionId,
175
176 pub available: BTreeMap<RoomVersionId, RoomVersionStability>,
178}
179
180impl RoomVersionsCapability {
181 pub fn new(
184 default: RoomVersionId,
185 available: BTreeMap<RoomVersionId, RoomVersionStability>,
186 ) -> Self {
187 Self { default, available }
188 }
189
190 pub fn is_default(&self) -> bool {
192 self.default == RoomVersionId::V1
193 && self.available.len() == 1
194 && self
195 .available
196 .get(&RoomVersionId::V1)
197 .map(|stability| *stability == RoomVersionStability::Stable)
198 .unwrap_or(false)
199 }
200}
201
202impl Default for RoomVersionsCapability {
203 fn default() -> Self {
204 Self {
205 default: RoomVersionId::V1,
206 available: btreemap! { RoomVersionId::V1 => RoomVersionStability::Stable },
207 }
208 }
209}
210
211#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
213#[derive(Clone, PartialEq, Eq, StringEnum)]
214#[ruma_enum(rename_all = "lowercase")]
215#[non_exhaustive]
216pub enum RoomVersionStability {
217 Stable,
219
220 Unstable,
222
223 #[doc(hidden)]
224 _Custom(PrivOwnedStr),
225}
226
227#[derive(Clone, Debug, Serialize, Deserialize)]
229#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
230pub struct SetDisplayNameCapability {
231 pub enabled: bool,
233}
234
235impl SetDisplayNameCapability {
236 pub fn new(enabled: bool) -> Self {
238 Self { enabled }
239 }
240
241 pub fn is_default(&self) -> bool {
243 self.enabled
244 }
245}
246
247impl Default for SetDisplayNameCapability {
248 fn default() -> Self {
249 Self { enabled: true }
250 }
251}
252
253#[derive(Clone, Debug, Serialize, Deserialize)]
255#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
256pub struct SetAvatarUrlCapability {
257 pub enabled: bool,
259}
260
261impl SetAvatarUrlCapability {
262 pub fn new(enabled: bool) -> Self {
264 Self { enabled }
265 }
266
267 pub fn is_default(&self) -> bool {
269 self.enabled
270 }
271}
272
273impl Default for SetAvatarUrlCapability {
274 fn default() -> Self {
275 Self { enabled: true }
276 }
277}
278
279#[derive(Clone, Debug, Serialize, Deserialize)]
281#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
282pub struct ThirdPartyIdChangesCapability {
283 pub enabled: bool,
286}
287
288impl ThirdPartyIdChangesCapability {
289 pub fn new(enabled: bool) -> Self {
291 Self { enabled }
292 }
293
294 pub fn is_default(&self) -> bool {
296 self.enabled
297 }
298}
299
300impl Default for ThirdPartyIdChangesCapability {
301 fn default() -> Self {
302 Self { enabled: true }
303 }
304}
305
306#[derive(Clone, Debug, Default, Serialize, Deserialize)]
308#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
309pub struct GetLoginTokenCapability {
310 pub enabled: bool,
312}
313
314impl GetLoginTokenCapability {
315 pub fn new(enabled: bool) -> Self {
317 Self { enabled }
318 }
319
320 pub fn is_default(&self) -> bool {
322 !self.enabled
323 }
324}
325
326#[cfg(test)]
327mod tests {
328 use std::borrow::Cow;
329
330 use assert_matches2::assert_matches;
331 use serde_json::json;
332
333 use super::Capabilities;
334
335 #[test]
336 fn capabilities_iter() -> serde_json::Result<()> {
337 let mut caps = Capabilities::new();
338 let custom_cap = json!({
339 "key": "value",
340 });
341 caps.set("m.some_random_capability", custom_cap)?;
342 let mut caps_iter = caps.iter();
343
344 let iter_res = caps_iter.next().unwrap();
345 assert_eq!(iter_res.name(), "m.change_password");
346 assert_eq!(iter_res.value(), Cow::Borrowed(&json!({ "enabled": true })));
347
348 let iter_res = caps_iter.next().unwrap();
349 assert_eq!(iter_res.name(), "m.room_versions");
350 assert_eq!(
351 iter_res.value(),
352 Cow::Borrowed(&json!({ "available": { "1": "stable" },"default" :"1" }))
353 );
354
355 let iter_res = caps_iter.next().unwrap();
356 assert_eq!(iter_res.name(), "m.set_displayname");
357 assert_eq!(iter_res.value(), Cow::Borrowed(&json!({ "enabled": true })));
358
359 let iter_res = caps_iter.next().unwrap();
360 assert_eq!(iter_res.name(), "m.set_avatar_url");
361 assert_eq!(iter_res.value(), Cow::Borrowed(&json!({ "enabled": true })));
362
363 let iter_res = caps_iter.next().unwrap();
364 assert_eq!(iter_res.name(), "m.3pid_changes");
365 assert_eq!(iter_res.value(), Cow::Borrowed(&json!({ "enabled": true })));
366
367 let iter_res = caps_iter.next().unwrap();
368 assert_eq!(iter_res.name(), "m.some_random_capability");
369 assert_eq!(iter_res.value(), Cow::Borrowed(&json!({ "key": "value" })));
370
371 assert_matches!(caps_iter.next(), None);
372 Ok(())
373 }
374}