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        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 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        /// Any other custom capabilities that the server supports outside of the specification,
137        /// labeled using the Java package naming convention and stored as arbitrary JSON values.
138        #[serde(flatten)]
139        custom_capabilities: BTreeMap<String, JsonValue>,
140    }
141
142    impl Capabilities {
143        /// Creates empty `Capabilities`.
144        pub fn new() -> Self {
145            Default::default()
146        }
147
148        /// Returns the value of the given capability.
149        ///
150        /// Prefer to use the public fields of `Capabilities` where possible; this method is meant
151        /// to be used for unsupported capabilities only.
152        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        /// Sets a capability to the given value.
171        ///
172        /// Prefer to use the public fields of `Capabilities` where possible; this method is meant
173        /// to be used for unsupported capabilities only and does not allow setting
174        /// arbitrary data for supported ones.
175        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    /// Information about the m.change_password capability
195    #[derive(Clone, Debug, Serialize, Deserialize)]
196    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
197    pub struct ChangePasswordCapability {
198        /// `true` if the user can change their password, `false` otherwise.
199        pub enabled: bool,
200    }
201
202    impl ChangePasswordCapability {
203        /// Creates a new `ChangePasswordCapability` with the given enabled flag.
204        pub fn new(enabled: bool) -> Self {
205            Self { enabled }
206        }
207
208        /// Returns whether all fields have their default value.
209        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    /// Information about the m.room_versions capability
221    #[derive(Clone, Debug, Serialize, Deserialize)]
222    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
223    pub struct RoomVersionsCapability {
224        /// The default room version the server is using for new rooms.
225        pub default: RoomVersionId,
226
227        /// A detailed description of the room versions the server supports.
228        pub available: BTreeMap<RoomVersionId, RoomVersionStability>,
229    }
230
231    impl RoomVersionsCapability {
232        /// Creates a new `RoomVersionsCapability` with the given default room version ID and room
233        /// version descriptions.
234        pub fn new(
235            default: RoomVersionId,
236            available: BTreeMap<RoomVersionId, RoomVersionStability>,
237        ) -> Self {
238            Self { default, available }
239        }
240
241        /// Returns whether all fields have their default value.
242        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    /// The stability of a room version.
263    #[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        /// Support for the given version is stable.
269        Stable,
270
271        /// Support for the given version is unstable.
272        Unstable,
273
274        #[doc(hidden)]
275        _Custom(PrivOwnedStr),
276    }
277
278    /// Information about the `m.set_displayname` capability
279    #[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        /// `true` if the user can change their display name, `false` otherwise.
284        pub enabled: bool,
285    }
286
287    #[allow(deprecated)]
288    impl SetDisplayNameCapability {
289        /// Creates a new `SetDisplayNameCapability` with the given enabled flag.
290        pub fn new(enabled: bool) -> Self {
291            Self { enabled }
292        }
293
294        /// Returns whether all fields have their default value.
295        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    /// Information about the `m.set_avatar_url` capability
308    #[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        /// `true` if the user can change their avatar, `false` otherwise.
313        pub enabled: bool,
314    }
315
316    #[allow(deprecated)]
317    impl SetAvatarUrlCapability {
318        /// Creates a new `SetAvatarUrlCapability` with the given enabled flag.
319        pub fn new(enabled: bool) -> Self {
320            Self { enabled }
321        }
322
323        /// Returns whether all fields have their default value.
324        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    /// Information about the `m.3pid_changes` capability
337    #[derive(Clone, Debug, Serialize, Deserialize)]
338    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
339    pub struct ThirdPartyIdChangesCapability {
340        /// `true` if the user can change the third-party identifiers associated with their
341        /// account, `false` otherwise.
342        pub enabled: bool,
343    }
344
345    impl ThirdPartyIdChangesCapability {
346        /// Creates a new `ThirdPartyIdChangesCapability` with the given enabled flag.
347        pub fn new(enabled: bool) -> Self {
348            Self { enabled }
349        }
350
351        /// Returns whether all fields have their default value.
352        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    /// Information about the `m.get_login_token` capability.
364    #[derive(Clone, Debug, Default, Serialize, Deserialize)]
365    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
366    pub struct GetLoginTokenCapability {
367        /// Whether the user can request a login token.
368        pub enabled: bool,
369    }
370
371    impl GetLoginTokenCapability {
372        /// Creates a new `GetLoginTokenCapability` with the given enabled flag.
373        pub fn new(enabled: bool) -> Self {
374            Self { enabled }
375        }
376
377        /// Returns whether all fields have their default value.
378        pub fn is_default(&self) -> bool {
379            !self.enabled
380        }
381    }
382
383    /// Information about the `m.profile_fields` capability.
384    #[derive(Clone, Debug, Default, Serialize, Deserialize)]
385    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
386    pub struct ProfileFieldsCapability {
387        /// Whether the user can set extended profile fields.
388        pub enabled: bool,
389
390        /// The fields that can be set by the user.
391        #[serde(skip_serializing_if = "Option::is_none")]
392        pub allowed: Option<Vec<ProfileFieldName>>,
393
394        /// The fields that cannot be set by the user.
395        ///
396        /// This list is ignored if `allowed` is provided.
397        #[serde(skip_serializing_if = "Option::is_none")]
398        pub disallowed: Option<Vec<ProfileFieldName>>,
399    }
400
401    impl ProfileFieldsCapability {
402        /// Creates a new `ProfileFieldsCapability` with the given enabled flag.
403        pub fn new(enabled: bool) -> Self {
404            Self { enabled, allowed: None, disallowed: None }
405        }
406
407        /// Whether the server advertises that the field with the given name can be set.
408        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                // The default is that any field is allowed.
419                true
420            }
421        }
422    }
423}