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, Metadata},
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    #[cfg(feature = "unstable-msc4133")]
28    use crate::profile::ProfileFieldName;
29    use crate::PrivOwnedStr;
30
31    const METADATA: Metadata = metadata! {
32        method: GET,
33        rate_limited: true,
34        authentication: AccessToken,
35        history: {
36            1.0 => "/_matrix/client/r0/capabilities",
37            1.1 => "/_matrix/client/v3/capabilities",
38        }
39    };
40
41    /// Request type for the `get_capabilities` endpoint.
42    #[request(error = crate::Error)]
43    #[derive(Default)]
44    pub struct Request {}
45
46    /// Response type for the `get_capabilities` endpoint.
47    #[response(error = crate::Error)]
48    pub struct Response {
49        /// The capabilities the server supports
50        pub capabilities: Capabilities,
51    }
52
53    impl Request {
54        /// Creates an empty `Request`.
55        pub fn new() -> Self {
56            Self {}
57        }
58    }
59
60    impl Response {
61        /// Creates a new `Response` with the given capabilities.
62        pub fn new(capabilities: Capabilities) -> Self {
63            Self { capabilities }
64        }
65    }
66
67    impl From<Capabilities> for Response {
68        fn from(capabilities: Capabilities) -> Self {
69            Self::new(capabilities)
70        }
71    }
72
73    /// Contains information about all the capabilities that the server supports.
74    #[derive(Clone, Debug, Default, Serialize, Deserialize)]
75    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
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        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        pub set_avatar_url: SetAvatarUrlCapability,
108
109        /// Capability to indicate if the user can change the third-party identifiers associated
110        /// with their account.
111        #[serde(
112            rename = "m.3pid_changes",
113            default,
114            skip_serializing_if = "ThirdPartyIdChangesCapability::is_default"
115        )]
116        pub thirdparty_id_changes: ThirdPartyIdChangesCapability,
117
118        /// Capability to indicate if the user can generate tokens to log further clients into
119        /// their account.
120        #[serde(
121            rename = "m.get_login_token",
122            default,
123            skip_serializing_if = "GetLoginTokenCapability::is_default"
124        )]
125        pub get_login_token: GetLoginTokenCapability,
126
127        /// Capability to indicate if the user can set extended profile fields.
128        #[cfg(feature = "unstable-msc4133")]
129        #[serde(
130            rename = "uk.tcpip.msc4133.profile_fields",
131            alias = "m.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                "m.set_displayname" => Some(Cow::Owned(serialize(&self.set_displayname))),
161                "m.set_avatar_url" => Some(Cow::Owned(serialize(&self.set_avatar_url))),
162                "m.3pid_changes" => Some(Cow::Owned(serialize(&self.thirdparty_id_changes))),
163                "m.get_login_token" => Some(Cow::Owned(serialize(&self.get_login_token))),
164                _ => self.custom_capabilities.get(capability).map(Cow::Borrowed),
165            }
166        }
167
168        /// Sets a capability to the given value.
169        ///
170        /// Prefer to use the public fields of `Capabilities` where possible; this method is meant
171        /// to be used for unsupported capabilities only and does not allow setting
172        /// arbitrary data for supported ones.
173        pub fn set(&mut self, capability: &str, value: JsonValue) -> serde_json::Result<()> {
174            match capability {
175                "m.change_password" => self.change_password = from_json_value(value)?,
176                "m.room_versions" => self.room_versions = from_json_value(value)?,
177                "m.set_displayname" => self.set_displayname = from_json_value(value)?,
178                "m.set_avatar_url" => self.set_avatar_url = from_json_value(value)?,
179                "m.3pid_changes" => self.thirdparty_id_changes = from_json_value(value)?,
180                "m.get_login_token" => self.get_login_token = from_json_value(value)?,
181                _ => {
182                    self.custom_capabilities.insert(capability.to_owned(), value);
183                }
184            }
185
186            Ok(())
187        }
188    }
189
190    /// Information about the m.change_password capability
191    #[derive(Clone, Debug, Serialize, Deserialize)]
192    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
193    pub struct ChangePasswordCapability {
194        /// `true` if the user can change their password, `false` otherwise.
195        pub enabled: bool,
196    }
197
198    impl ChangePasswordCapability {
199        /// Creates a new `ChangePasswordCapability` with the given enabled flag.
200        pub fn new(enabled: bool) -> Self {
201            Self { enabled }
202        }
203
204        /// Returns whether all fields have their default value.
205        pub fn is_default(&self) -> bool {
206            self.enabled
207        }
208    }
209
210    impl Default for ChangePasswordCapability {
211        fn default() -> Self {
212            Self { enabled: true }
213        }
214    }
215
216    /// Information about the m.room_versions capability
217    #[derive(Clone, Debug, Serialize, Deserialize)]
218    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
219    pub struct RoomVersionsCapability {
220        /// The default room version the server is using for new rooms.
221        pub default: RoomVersionId,
222
223        /// A detailed description of the room versions the server supports.
224        pub available: BTreeMap<RoomVersionId, RoomVersionStability>,
225    }
226
227    impl RoomVersionsCapability {
228        /// Creates a new `RoomVersionsCapability` with the given default room version ID and room
229        /// version descriptions.
230        pub fn new(
231            default: RoomVersionId,
232            available: BTreeMap<RoomVersionId, RoomVersionStability>,
233        ) -> Self {
234            Self { default, available }
235        }
236
237        /// Returns whether all fields have their default value.
238        pub fn is_default(&self) -> bool {
239            self.default == RoomVersionId::V1
240                && self.available.len() == 1
241                && self
242                    .available
243                    .get(&RoomVersionId::V1)
244                    .map(|stability| *stability == RoomVersionStability::Stable)
245                    .unwrap_or(false)
246        }
247    }
248
249    impl Default for RoomVersionsCapability {
250        fn default() -> Self {
251            Self {
252                default: RoomVersionId::V1,
253                available: btreemap! { RoomVersionId::V1 => RoomVersionStability::Stable },
254            }
255        }
256    }
257
258    /// The stability of a room version.
259    #[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
260    #[derive(Clone, PartialEq, Eq, StringEnum)]
261    #[ruma_enum(rename_all = "lowercase")]
262    #[non_exhaustive]
263    pub enum RoomVersionStability {
264        /// Support for the given version is stable.
265        Stable,
266
267        /// Support for the given version is unstable.
268        Unstable,
269
270        #[doc(hidden)]
271        _Custom(PrivOwnedStr),
272    }
273
274    /// Information about the `m.set_displayname` capability
275    #[derive(Clone, Debug, Serialize, Deserialize)]
276    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
277    pub struct SetDisplayNameCapability {
278        /// `true` if the user can change their display name, `false` otherwise.
279        pub enabled: bool,
280    }
281
282    impl SetDisplayNameCapability {
283        /// Creates a new `SetDisplayNameCapability` with the given enabled flag.
284        pub fn new(enabled: bool) -> Self {
285            Self { enabled }
286        }
287
288        /// Returns whether all fields have their default value.
289        pub fn is_default(&self) -> bool {
290            self.enabled
291        }
292    }
293
294    impl Default for SetDisplayNameCapability {
295        fn default() -> Self {
296            Self { enabled: true }
297        }
298    }
299
300    /// Information about the `m.set_avatar_url` capability
301    #[derive(Clone, Debug, Serialize, Deserialize)]
302    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
303    pub struct SetAvatarUrlCapability {
304        /// `true` if the user can change their avatar, `false` otherwise.
305        pub enabled: bool,
306    }
307
308    impl SetAvatarUrlCapability {
309        /// Creates a new `SetAvatarUrlCapability` with the given enabled flag.
310        pub fn new(enabled: bool) -> Self {
311            Self { enabled }
312        }
313
314        /// Returns whether all fields have their default value.
315        pub fn is_default(&self) -> bool {
316            self.enabled
317        }
318    }
319
320    impl Default for SetAvatarUrlCapability {
321        fn default() -> Self {
322            Self { enabled: true }
323        }
324    }
325
326    /// Information about the `m.3pid_changes` capability
327    #[derive(Clone, Debug, Serialize, Deserialize)]
328    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
329    pub struct ThirdPartyIdChangesCapability {
330        /// `true` if the user can change the third-party identifiers associated with their
331        /// account, `false` otherwise.
332        pub enabled: bool,
333    }
334
335    impl ThirdPartyIdChangesCapability {
336        /// Creates a new `ThirdPartyIdChangesCapability` with the given enabled flag.
337        pub fn new(enabled: bool) -> Self {
338            Self { enabled }
339        }
340
341        /// Returns whether all fields have their default value.
342        pub fn is_default(&self) -> bool {
343            self.enabled
344        }
345    }
346
347    impl Default for ThirdPartyIdChangesCapability {
348        fn default() -> Self {
349            Self { enabled: true }
350        }
351    }
352
353    /// Information about the `m.get_login_token` capability.
354    #[derive(Clone, Debug, Default, Serialize, Deserialize)]
355    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
356    pub struct GetLoginTokenCapability {
357        /// Whether the user can request a login token.
358        pub enabled: bool,
359    }
360
361    impl GetLoginTokenCapability {
362        /// Creates a new `GetLoginTokenCapability` with the given enabled flag.
363        pub fn new(enabled: bool) -> Self {
364            Self { enabled }
365        }
366
367        /// Returns whether all fields have their default value.
368        pub fn is_default(&self) -> bool {
369            !self.enabled
370        }
371    }
372
373    /// Information about the `m.profile_fields` capability.
374    #[cfg(feature = "unstable-msc4133")]
375    #[derive(Clone, Debug, Default, Serialize, Deserialize)]
376    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
377    pub struct ProfileFieldsCapability {
378        /// Whether the user can set extended profile fields.
379        pub enabled: bool,
380
381        /// The fields that can be set by the user.
382        #[serde(skip_serializing_if = "Option::is_none")]
383        pub allowed: Option<Vec<ProfileFieldName>>,
384
385        /// The fields that cannot be set by the user.
386        ///
387        /// This list is ignored if `allowed` is provided.
388        #[serde(skip_serializing_if = "Option::is_none")]
389        pub disallowed: Option<Vec<ProfileFieldName>>,
390    }
391
392    #[cfg(feature = "unstable-msc4133")]
393    impl ProfileFieldsCapability {
394        /// Creates a new `ProfileFieldsCapability` with the given enabled flag.
395        pub fn new(enabled: bool) -> Self {
396            Self { enabled, allowed: None, disallowed: None }
397        }
398
399        /// Whether the server advertises that the field with the given name can be set.
400        pub fn can_set_field(&self, field: &ProfileFieldName) -> bool {
401            if !self.enabled {
402                return false;
403            }
404
405            if let Some(allowed) = &self.allowed {
406                allowed.contains(field)
407            } else if let Some(disallowed) = &self.disallowed {
408                !disallowed.contains(field)
409            } else {
410                // The default is that any field is allowed.
411                true
412            }
413        }
414    }
415}