ruma_client_api/discovery/
get_capabilities.rs1pub mod v3 {
9 use std::{borrow::Cow, collections::BTreeMap};
14
15 use maplit::btreemap;
16 use ruma_common::{
17 api::{request, response},
18 metadata,
19 serde::StringEnum,
20 RoomVersionId,
21 };
22 use serde::{Deserialize, Serialize};
23 use serde_json::{
24 from_value as from_json_value, to_value as to_json_value, Value as JsonValue,
25 };
26
27 use crate::{profile::ProfileFieldName, PrivOwnedStr};
28
29 metadata! {
30 method: GET,
31 rate_limited: true,
32 authentication: AccessToken,
33 history: {
34 1.0 => "/_matrix/client/r0/capabilities",
35 1.1 => "/_matrix/client/v3/capabilities",
36 }
37 }
38
39 #[request(error = crate::Error)]
41 #[derive(Default)]
42 pub struct Request {}
43
44 #[response(error = crate::Error)]
46 pub struct Response {
47 pub capabilities: Capabilities,
49 }
50
51 impl Request {
52 pub fn new() -> Self {
54 Self {}
55 }
56 }
57
58 impl Response {
59 pub fn new(capabilities: Capabilities) -> Self {
61 Self { capabilities }
62 }
63 }
64
65 impl From<Capabilities> for Response {
66 fn from(capabilities: Capabilities) -> Self {
67 Self::new(capabilities)
68 }
69 }
70
71 #[derive(Clone, Debug, Default, Serialize, Deserialize)]
73 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
74 #[allow(deprecated)]
75 pub struct Capabilities {
76 #[serde(
78 rename = "m.change_password",
79 default,
80 skip_serializing_if = "ChangePasswordCapability::is_default"
81 )]
82 pub change_password: ChangePasswordCapability,
83
84 #[serde(
86 rename = "m.room_versions",
87 default,
88 skip_serializing_if = "RoomVersionsCapability::is_default"
89 )]
90 pub room_versions: RoomVersionsCapability,
91
92 #[serde(
94 rename = "m.set_displayname",
95 default,
96 skip_serializing_if = "SetDisplayNameCapability::is_default"
97 )]
98 #[deprecated = "Since Matrix 1.16, prefer profile_fields if it is set."]
99 pub set_displayname: SetDisplayNameCapability,
100
101 #[serde(
103 rename = "m.set_avatar_url",
104 default,
105 skip_serializing_if = "SetAvatarUrlCapability::is_default"
106 )]
107 #[deprecated = "Since Matrix 1.16, prefer profile_fields if it is set."]
108 pub set_avatar_url: SetAvatarUrlCapability,
109
110 #[serde(
113 rename = "m.3pid_changes",
114 default,
115 skip_serializing_if = "ThirdPartyIdChangesCapability::is_default"
116 )]
117 pub thirdparty_id_changes: ThirdPartyIdChangesCapability,
118
119 #[serde(
122 rename = "m.get_login_token",
123 default,
124 skip_serializing_if = "GetLoginTokenCapability::is_default"
125 )]
126 pub get_login_token: GetLoginTokenCapability,
127
128 #[serde(
130 rename = "m.profile_fields",
131 alias = "uk.tcpip.msc4133.profile_fields",
132 skip_serializing_if = "Option::is_none"
133 )]
134 pub profile_fields: Option<ProfileFieldsCapability>,
135
136 #[serde(flatten)]
139 custom_capabilities: BTreeMap<String, JsonValue>,
140 }
141
142 impl Capabilities {
143 pub fn new() -> Self {
145 Default::default()
146 }
147
148 pub fn get(&self, capability: &str) -> Option<Cow<'_, JsonValue>> {
153 fn serialize<T: Serialize>(cap: &T) -> JsonValue {
154 to_json_value(cap).expect("capability serialization to succeed")
155 }
156
157 match capability {
158 "m.change_password" => Some(Cow::Owned(serialize(&self.change_password))),
159 "m.room_versions" => Some(Cow::Owned(serialize(&self.room_versions))),
160 #[allow(deprecated)]
161 "m.set_displayname" => Some(Cow::Owned(serialize(&self.set_displayname))),
162 #[allow(deprecated)]
163 "m.set_avatar_url" => Some(Cow::Owned(serialize(&self.set_avatar_url))),
164 "m.3pid_changes" => Some(Cow::Owned(serialize(&self.thirdparty_id_changes))),
165 "m.get_login_token" => Some(Cow::Owned(serialize(&self.get_login_token))),
166 _ => self.custom_capabilities.get(capability).map(Cow::Borrowed),
167 }
168 }
169
170 pub fn set(&mut self, capability: &str, value: JsonValue) -> serde_json::Result<()> {
176 match capability {
177 "m.change_password" => self.change_password = from_json_value(value)?,
178 "m.room_versions" => self.room_versions = from_json_value(value)?,
179 #[allow(deprecated)]
180 "m.set_displayname" => self.set_displayname = from_json_value(value)?,
181 #[allow(deprecated)]
182 "m.set_avatar_url" => self.set_avatar_url = from_json_value(value)?,
183 "m.3pid_changes" => self.thirdparty_id_changes = from_json_value(value)?,
184 "m.get_login_token" => self.get_login_token = from_json_value(value)?,
185 _ => {
186 self.custom_capabilities.insert(capability.to_owned(), value);
187 }
188 }
189
190 Ok(())
191 }
192 }
193
194 #[derive(Clone, Debug, Serialize, Deserialize)]
196 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
197 pub struct ChangePasswordCapability {
198 pub enabled: bool,
200 }
201
202 impl ChangePasswordCapability {
203 pub fn new(enabled: bool) -> Self {
205 Self { enabled }
206 }
207
208 pub fn is_default(&self) -> bool {
210 self.enabled
211 }
212 }
213
214 impl Default for ChangePasswordCapability {
215 fn default() -> Self {
216 Self { enabled: true }
217 }
218 }
219
220 #[derive(Clone, Debug, Serialize, Deserialize)]
222 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
223 pub struct RoomVersionsCapability {
224 pub default: RoomVersionId,
226
227 pub available: BTreeMap<RoomVersionId, RoomVersionStability>,
229 }
230
231 impl RoomVersionsCapability {
232 pub fn new(
235 default: RoomVersionId,
236 available: BTreeMap<RoomVersionId, RoomVersionStability>,
237 ) -> Self {
238 Self { default, available }
239 }
240
241 pub fn is_default(&self) -> bool {
243 self.default == RoomVersionId::V1
244 && self.available.len() == 1
245 && self
246 .available
247 .get(&RoomVersionId::V1)
248 .map(|stability| *stability == RoomVersionStability::Stable)
249 .unwrap_or(false)
250 }
251 }
252
253 impl Default for RoomVersionsCapability {
254 fn default() -> Self {
255 Self {
256 default: RoomVersionId::V1,
257 available: btreemap! { RoomVersionId::V1 => RoomVersionStability::Stable },
258 }
259 }
260 }
261
262 #[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
264 #[derive(Clone, StringEnum)]
265 #[ruma_enum(rename_all = "lowercase")]
266 #[non_exhaustive]
267 pub enum RoomVersionStability {
268 Stable,
270
271 Unstable,
273
274 #[doc(hidden)]
275 _Custom(PrivOwnedStr),
276 }
277
278 #[derive(Clone, Debug, Serialize, Deserialize)]
280 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
281 #[deprecated = "Since Matrix 1.16, prefer ProfileFieldsCapability instead."]
282 pub struct SetDisplayNameCapability {
283 pub enabled: bool,
285 }
286
287 #[allow(deprecated)]
288 impl SetDisplayNameCapability {
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
300 #[allow(deprecated)]
301 impl Default for SetDisplayNameCapability {
302 fn default() -> Self {
303 Self { enabled: true }
304 }
305 }
306
307 #[derive(Clone, Debug, Serialize, Deserialize)]
309 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
310 #[deprecated = "Since Matrix 1.16, prefer ProfileFieldsCapability instead."]
311 pub struct SetAvatarUrlCapability {
312 pub enabled: bool,
314 }
315
316 #[allow(deprecated)]
317 impl SetAvatarUrlCapability {
318 pub fn new(enabled: bool) -> Self {
320 Self { enabled }
321 }
322
323 pub fn is_default(&self) -> bool {
325 self.enabled
326 }
327 }
328
329 #[allow(deprecated)]
330 impl Default for SetAvatarUrlCapability {
331 fn default() -> Self {
332 Self { enabled: true }
333 }
334 }
335
336 #[derive(Clone, Debug, Serialize, Deserialize)]
338 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
339 pub struct ThirdPartyIdChangesCapability {
340 pub enabled: bool,
343 }
344
345 impl ThirdPartyIdChangesCapability {
346 pub fn new(enabled: bool) -> Self {
348 Self { enabled }
349 }
350
351 pub fn is_default(&self) -> bool {
353 self.enabled
354 }
355 }
356
357 impl Default for ThirdPartyIdChangesCapability {
358 fn default() -> Self {
359 Self { enabled: true }
360 }
361 }
362
363 #[derive(Clone, Debug, Default, Serialize, Deserialize)]
365 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
366 pub struct GetLoginTokenCapability {
367 pub enabled: bool,
369 }
370
371 impl GetLoginTokenCapability {
372 pub fn new(enabled: bool) -> Self {
374 Self { enabled }
375 }
376
377 pub fn is_default(&self) -> bool {
379 !self.enabled
380 }
381 }
382
383 #[derive(Clone, Debug, Default, Serialize, Deserialize)]
385 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
386 pub struct ProfileFieldsCapability {
387 pub enabled: bool,
389
390 #[serde(skip_serializing_if = "Option::is_none")]
392 pub allowed: Option<Vec<ProfileFieldName>>,
393
394 #[serde(skip_serializing_if = "Option::is_none")]
398 pub disallowed: Option<Vec<ProfileFieldName>>,
399 }
400
401 impl ProfileFieldsCapability {
402 pub fn new(enabled: bool) -> Self {
404 Self { enabled, allowed: None, disallowed: None }
405 }
406
407 pub fn can_set_field(&self, field: &ProfileFieldName) -> bool {
409 if !self.enabled {
410 return false;
411 }
412
413 if let Some(allowed) = &self.allowed {
414 allowed.contains(field)
415 } else if let Some(disallowed) = &self.disallowed {
416 !disallowed.contains(field)
417 } else {
418 true
420 }
421 }
422 }
423}