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