Skip to main content

ruma_client_api/discovery/
get_capabilities.rs

1//! `GET /_matrix/client/*/capabilities`
2//!
3//! Get information about the server's supported feature set and other relevant capabilities
4//! ([spec]).
5//!
6//! [spec]: https://spec.matrix.org/v1.18/client-server-api/#capabilities-negotiation
7
8pub mod v3 {
9    //! `/v3/` ([spec])
10    //!
11    //! [spec]: https://spec.matrix.org/v1.18/client-server-api/#get_matrixclientv3capabilities
12
13    use std::{borrow::Cow, collections::BTreeMap};
14
15    use maplit::btreemap;
16    use ruma_common::{
17        RoomVersionId,
18        api::{auth_scheme::AccessToken, request, response},
19        metadata,
20        profile::ProfileFieldName,
21        serde::StringEnum,
22    };
23    use serde::{Deserialize, Serialize};
24    use serde_json::{
25        Value as JsonValue, from_value as from_json_value, to_value as to_json_value,
26    };
27
28    use crate::PrivOwnedStr;
29
30    metadata! {
31        method: GET,
32        rate_limited: true,
33        authentication: AccessToken,
34        history: {
35            1.0 => "/_matrix/client/r0/capabilities",
36            1.1 => "/_matrix/client/v3/capabilities",
37        }
38    }
39
40    /// Request type for the `get_capabilities` endpoint.
41    #[request]
42    #[derive(Default)]
43    pub struct Request {}
44
45    /// Response type for the `get_capabilities` endpoint.
46    #[response]
47    pub struct Response {
48        /// The capabilities the server supports
49        pub capabilities: Capabilities,
50    }
51
52    impl Request {
53        /// Creates an empty `Request`.
54        pub fn new() -> Self {
55            Self {}
56        }
57    }
58
59    impl Response {
60        /// Creates a new `Response` with the given capabilities.
61        pub fn new(capabilities: Capabilities) -> Self {
62            Self { capabilities }
63        }
64    }
65
66    impl From<Capabilities> for Response {
67        fn from(capabilities: Capabilities) -> Self {
68            Self::new(capabilities)
69        }
70    }
71
72    /// Contains information about all the capabilities that the server supports.
73    #[derive(Clone, Debug, Default, Serialize, Deserialize)]
74    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
75    #[allow(deprecated)]
76    pub struct Capabilities {
77        /// Capability to indicate if the user can change their password.
78        #[serde(
79            rename = "m.change_password",
80            default,
81            skip_serializing_if = "ChangePasswordCapability::is_default"
82        )]
83        pub change_password: ChangePasswordCapability,
84
85        /// The room versions the server supports.
86        #[serde(
87            rename = "m.room_versions",
88            default,
89            skip_serializing_if = "RoomVersionsCapability::is_default"
90        )]
91        pub room_versions: RoomVersionsCapability,
92
93        /// Capability to indicate if the user can change their display name.
94        #[serde(
95            rename = "m.set_displayname",
96            default,
97            skip_serializing_if = "SetDisplayNameCapability::is_default"
98        )]
99        #[deprecated = "Since Matrix 1.16, prefer profile_fields if it is set."]
100        pub set_displayname: SetDisplayNameCapability,
101
102        /// Capability to indicate if the user can change their avatar.
103        #[serde(
104            rename = "m.set_avatar_url",
105            default,
106            skip_serializing_if = "SetAvatarUrlCapability::is_default"
107        )]
108        #[deprecated = "Since Matrix 1.16, prefer profile_fields if it is set."]
109        pub set_avatar_url: SetAvatarUrlCapability,
110
111        /// Capability to indicate if the user can change the third-party identifiers associated
112        /// with their account.
113        #[serde(
114            rename = "m.3pid_changes",
115            default,
116            skip_serializing_if = "ThirdPartyIdChangesCapability::is_default"
117        )]
118        pub thirdparty_id_changes: ThirdPartyIdChangesCapability,
119
120        /// Capability to indicate if the user can generate tokens to log further clients into
121        /// their account.
122        #[serde(
123            rename = "m.get_login_token",
124            default,
125            skip_serializing_if = "GetLoginTokenCapability::is_default"
126        )]
127        pub get_login_token: GetLoginTokenCapability,
128
129        /// Capability to indicate if the user can set extended profile fields.
130        #[serde(
131            rename = "m.profile_fields",
132            alias = "uk.tcpip.msc4133.profile_fields",
133            skip_serializing_if = "Option::is_none"
134        )]
135        pub profile_fields: Option<ProfileFieldsCapability>,
136
137        /// Capability to indicate if the server automatically forgets rooms that the user leaves.
138        #[serde(
139            rename = "m.forget_forced_upon_leave",
140            default,
141            skip_serializing_if = "ForgetForcedUponLeaveCapability::is_default"
142        )]
143        pub forget_forced_upon_leave: ForgetForcedUponLeaveCapability,
144
145        /// Capability to indicate if the user can perform account moderation actions via [server
146        /// administration] endpoints.
147        ///
148        /// [server administration]: https://spec.matrix.org/v1.18/client-server-api/#server-administration
149        #[serde(
150            rename = "m.account_moderation",
151            default,
152            skip_serializing_if = "AccountModerationCapability::is_default"
153        )]
154        pub account_moderation: AccountModerationCapability,
155
156        /// Any other custom capabilities that the server supports outside of the specification,
157        /// labeled using the Java package naming convention and stored as arbitrary JSON values.
158        #[serde(flatten)]
159        custom_capabilities: BTreeMap<String, JsonValue>,
160    }
161
162    impl Capabilities {
163        /// Creates empty `Capabilities`.
164        pub fn new() -> Self {
165            Default::default()
166        }
167
168        /// Returns the value of the given capability.
169        ///
170        /// Prefer to use the public fields of `Capabilities` where possible; this method is meant
171        /// to be used for unsupported capabilities only.
172        pub fn get(&self, capability: &str) -> Option<Cow<'_, JsonValue>> {
173            fn serialize<T: Serialize>(cap: &T) -> JsonValue {
174                to_json_value(cap).expect("capability serialization to succeed")
175            }
176
177            match capability {
178                "m.change_password" => Some(Cow::Owned(serialize(&self.change_password))),
179                "m.room_versions" => Some(Cow::Owned(serialize(&self.room_versions))),
180                #[allow(deprecated)]
181                "m.set_displayname" => Some(Cow::Owned(serialize(&self.set_displayname))),
182                #[allow(deprecated)]
183                "m.set_avatar_url" => Some(Cow::Owned(serialize(&self.set_avatar_url))),
184                "m.3pid_changes" => Some(Cow::Owned(serialize(&self.thirdparty_id_changes))),
185                "m.get_login_token" => Some(Cow::Owned(serialize(&self.get_login_token))),
186                "m.forget_forced_upon_leave" => {
187                    Some(Cow::Owned(serialize(&self.forget_forced_upon_leave)))
188                }
189                "m.account_moderation" => Some(Cow::Owned(serialize(&self.account_moderation))),
190                _ => self.custom_capabilities.get(capability).map(Cow::Borrowed),
191            }
192        }
193
194        /// Sets a capability to the given value.
195        ///
196        /// Prefer to use the public fields of `Capabilities` where possible; this method is meant
197        /// to be used for unsupported capabilities only and does not allow setting
198        /// arbitrary data for supported ones.
199        pub fn set(&mut self, capability: &str, value: JsonValue) -> serde_json::Result<()> {
200            match capability {
201                "m.change_password" => self.change_password = from_json_value(value)?,
202                "m.room_versions" => self.room_versions = from_json_value(value)?,
203                #[allow(deprecated)]
204                "m.set_displayname" => self.set_displayname = from_json_value(value)?,
205                #[allow(deprecated)]
206                "m.set_avatar_url" => self.set_avatar_url = from_json_value(value)?,
207                "m.3pid_changes" => self.thirdparty_id_changes = from_json_value(value)?,
208                "m.get_login_token" => self.get_login_token = from_json_value(value)?,
209                "m.forget_forced_upon_leave" => {
210                    self.forget_forced_upon_leave = from_json_value(value)?;
211                }
212                "m.account_moderation" => {
213                    self.account_moderation = from_json_value(value)?;
214                }
215                _ => {
216                    self.custom_capabilities.insert(capability.to_owned(), value);
217                }
218            }
219
220            Ok(())
221        }
222    }
223
224    /// Information about the m.change_password capability
225    #[derive(Clone, Debug, Serialize, Deserialize)]
226    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
227    pub struct ChangePasswordCapability {
228        /// `true` if the user can change their password, `false` otherwise.
229        pub enabled: bool,
230    }
231
232    impl ChangePasswordCapability {
233        /// Creates a new `ChangePasswordCapability` with the given enabled flag.
234        pub fn new(enabled: bool) -> Self {
235            Self { enabled }
236        }
237
238        /// Returns whether all fields have their default value.
239        pub fn is_default(&self) -> bool {
240            self.enabled
241        }
242    }
243
244    impl Default for ChangePasswordCapability {
245        fn default() -> Self {
246            Self { enabled: true }
247        }
248    }
249
250    /// Information about the m.room_versions capability
251    #[derive(Clone, Debug, Serialize, Deserialize)]
252    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
253    pub struct RoomVersionsCapability {
254        /// The default room version the server is using for new rooms.
255        pub default: RoomVersionId,
256
257        /// A detailed description of the room versions the server supports.
258        pub available: BTreeMap<RoomVersionId, RoomVersionStability>,
259    }
260
261    impl RoomVersionsCapability {
262        /// Creates a new `RoomVersionsCapability` with the given default room version ID and room
263        /// version descriptions.
264        pub fn new(
265            default: RoomVersionId,
266            available: BTreeMap<RoomVersionId, RoomVersionStability>,
267        ) -> Self {
268            Self { default, available }
269        }
270
271        /// Returns whether all fields have their default value.
272        pub fn is_default(&self) -> bool {
273            self.default == RoomVersionId::V1
274                && self.available.len() == 1
275                && self
276                    .available
277                    .get(&RoomVersionId::V1)
278                    .map(|stability| *stability == RoomVersionStability::Stable)
279                    .unwrap_or(false)
280        }
281    }
282
283    impl Default for RoomVersionsCapability {
284        fn default() -> Self {
285            Self {
286                default: RoomVersionId::V1,
287                available: btreemap! { RoomVersionId::V1 => RoomVersionStability::Stable },
288            }
289        }
290    }
291
292    /// The stability of a room version.
293    #[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
294    #[derive(Clone, StringEnum)]
295    #[ruma_enum(rename_all = "lowercase")]
296    #[non_exhaustive]
297    pub enum RoomVersionStability {
298        /// Support for the given version is stable.
299        Stable,
300
301        /// Support for the given version is unstable.
302        Unstable,
303
304        #[doc(hidden)]
305        _Custom(PrivOwnedStr),
306    }
307
308    /// Information about the `m.set_displayname` capability
309    #[derive(Clone, Debug, Serialize, Deserialize)]
310    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
311    #[deprecated = "Since Matrix 1.16, prefer ProfileFieldsCapability instead."]
312    pub struct SetDisplayNameCapability {
313        /// `true` if the user can change their display name, `false` otherwise.
314        pub enabled: bool,
315    }
316
317    #[allow(deprecated)]
318    impl SetDisplayNameCapability {
319        /// Creates a new `SetDisplayNameCapability` with the given enabled flag.
320        pub fn new(enabled: bool) -> Self {
321            Self { enabled }
322        }
323
324        /// Returns whether all fields have their default value.
325        pub fn is_default(&self) -> bool {
326            self.enabled
327        }
328    }
329
330    #[allow(deprecated)]
331    impl Default for SetDisplayNameCapability {
332        fn default() -> Self {
333            Self { enabled: true }
334        }
335    }
336
337    /// Information about the `m.set_avatar_url` capability
338    #[derive(Clone, Debug, Serialize, Deserialize)]
339    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
340    #[deprecated = "Since Matrix 1.16, prefer ProfileFieldsCapability instead."]
341    pub struct SetAvatarUrlCapability {
342        /// `true` if the user can change their avatar, `false` otherwise.
343        pub enabled: bool,
344    }
345
346    #[allow(deprecated)]
347    impl SetAvatarUrlCapability {
348        /// Creates a new `SetAvatarUrlCapability` with the given enabled flag.
349        pub fn new(enabled: bool) -> Self {
350            Self { enabled }
351        }
352
353        /// Returns whether all fields have their default value.
354        pub fn is_default(&self) -> bool {
355            self.enabled
356        }
357    }
358
359    #[allow(deprecated)]
360    impl Default for SetAvatarUrlCapability {
361        fn default() -> Self {
362            Self { enabled: true }
363        }
364    }
365
366    /// Information about the `m.3pid_changes` capability
367    #[derive(Clone, Debug, Serialize, Deserialize)]
368    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
369    pub struct ThirdPartyIdChangesCapability {
370        /// `true` if the user can change the third-party identifiers associated with their
371        /// account, `false` otherwise.
372        pub enabled: bool,
373    }
374
375    impl ThirdPartyIdChangesCapability {
376        /// Creates a new `ThirdPartyIdChangesCapability` with the given enabled flag.
377        pub fn new(enabled: bool) -> Self {
378            Self { enabled }
379        }
380
381        /// Returns whether all fields have their default value.
382        pub fn is_default(&self) -> bool {
383            self.enabled
384        }
385    }
386
387    impl Default for ThirdPartyIdChangesCapability {
388        fn default() -> Self {
389            Self { enabled: true }
390        }
391    }
392
393    /// Information about the `m.get_login_token` capability.
394    #[derive(Clone, Debug, Default, Serialize, Deserialize)]
395    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
396    pub struct GetLoginTokenCapability {
397        /// Whether the user can request a login token.
398        pub enabled: bool,
399    }
400
401    impl GetLoginTokenCapability {
402        /// Creates a new `GetLoginTokenCapability` with the given enabled flag.
403        pub fn new(enabled: bool) -> Self {
404            Self { enabled }
405        }
406
407        /// Returns whether all fields have their default value.
408        pub fn is_default(&self) -> bool {
409            !self.enabled
410        }
411    }
412
413    /// Information about the `m.profile_fields` capability.
414    #[derive(Clone, Debug, Default, Serialize, Deserialize)]
415    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
416    pub struct ProfileFieldsCapability {
417        /// Whether the user can set extended profile fields.
418        pub enabled: bool,
419
420        /// The fields that can be set by the user.
421        #[serde(skip_serializing_if = "Option::is_none")]
422        pub allowed: Option<Vec<ProfileFieldName>>,
423
424        /// The fields that cannot be set by the user.
425        ///
426        /// This list is ignored if `allowed` is provided.
427        #[serde(skip_serializing_if = "Option::is_none")]
428        pub disallowed: Option<Vec<ProfileFieldName>>,
429    }
430
431    impl ProfileFieldsCapability {
432        /// Creates a new `ProfileFieldsCapability` with the given enabled flag.
433        pub fn new(enabled: bool) -> Self {
434            Self { enabled, allowed: None, disallowed: None }
435        }
436
437        /// Whether the server advertises that the field with the given name can be set.
438        pub fn can_set_field(&self, field: &ProfileFieldName) -> bool {
439            if !self.enabled {
440                return false;
441            }
442
443            if let Some(allowed) = &self.allowed {
444                allowed.contains(field)
445            } else if let Some(disallowed) = &self.disallowed {
446                !disallowed.contains(field)
447            } else {
448                // The default is that any field is allowed.
449                true
450            }
451        }
452    }
453
454    /// Information about the [`m.forget_forced_upon_leave`] capability.
455    ///
456    /// [`m.forget_forced_upon_leave`]: https://spec.matrix.org/v1.18/client-server-api/#mforget_forced_upon_leave-capability
457    #[derive(Clone, Debug, Default, Serialize, Deserialize)]
458    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
459    pub struct ForgetForcedUponLeaveCapability {
460        /// Whether the server will automatically forget any room that the user leaves.
461        ///
462        /// This behavior applies irrespective of whether the user has left the room on their own
463        /// or has been kicked or banned from the room by another user.
464        pub enabled: bool,
465    }
466
467    impl ForgetForcedUponLeaveCapability {
468        /// Creates a new `ForgetForcedUponLeaveCapability` with the given enabled flag.
469        pub fn new(enabled: bool) -> Self {
470            Self { enabled }
471        }
472
473        /// Returns whether all fields have their default value.
474        pub fn is_default(&self) -> bool {
475            !self.enabled
476        }
477    }
478
479    /// Information about the `m.account_moderation` capability.
480    #[derive(Clone, Debug, Default, Serialize, Deserialize)]
481    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
482    pub struct AccountModerationCapability {
483        /// Whether the user can suspend a user via `PUT /admin/suspend/{userId}`.
484        #[serde(default, skip_serializing_if = "ruma_common::serde::is_default")]
485        pub suspend: bool,
486
487        /// Whether the user can lock a user via `PUT /admin/lock/{userId}`.
488        #[serde(default, skip_serializing_if = "ruma_common::serde::is_default")]
489        pub lock: bool,
490    }
491
492    impl AccountModerationCapability {
493        /// Creates a new `AccountModerationCapability` with the given suspend and lock
494        /// capabilities.
495        pub fn new(suspend: bool, lock: bool) -> Self {
496            Self { suspend, lock }
497        }
498
499        /// Returns whether all fields have their default value.
500        pub fn is_default(&self) -> bool {
501            !self.suspend && !self.lock
502        }
503    }
504}