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    use crate::PrivOwnedStr;
28
29    const METADATA: Metadata = 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    pub struct Capabilities {
75        /// Capability to indicate if the user can change their password.
76        #[serde(
77            rename = "m.change_password",
78            default,
79            skip_serializing_if = "ChangePasswordCapability::is_default"
80        )]
81        pub change_password: ChangePasswordCapability,
82
83        /// The room versions the server supports.
84        #[serde(
85            rename = "m.room_versions",
86            default,
87            skip_serializing_if = "RoomVersionsCapability::is_default"
88        )]
89        pub room_versions: RoomVersionsCapability,
90
91        /// Capability to indicate if the user can change their display name.
92        #[serde(
93            rename = "m.set_displayname",
94            default,
95            skip_serializing_if = "SetDisplayNameCapability::is_default"
96        )]
97        pub set_displayname: SetDisplayNameCapability,
98
99        /// Capability to indicate if the user can change their avatar.
100        #[serde(
101            rename = "m.set_avatar_url",
102            default,
103            skip_serializing_if = "SetAvatarUrlCapability::is_default"
104        )]
105        pub set_avatar_url: SetAvatarUrlCapability,
106
107        /// Capability to indicate if the user can change the third-party identifiers associated
108        /// with their account.
109        #[serde(
110            rename = "m.3pid_changes",
111            default,
112            skip_serializing_if = "ThirdPartyIdChangesCapability::is_default"
113        )]
114        pub thirdparty_id_changes: ThirdPartyIdChangesCapability,
115
116        /// Capability to indicate if the user can generate tokens to log further clients into
117        /// their account.
118        #[serde(
119            rename = "m.get_login_token",
120            default,
121            skip_serializing_if = "GetLoginTokenCapability::is_default"
122        )]
123        pub get_login_token: GetLoginTokenCapability,
124
125        /// Any other custom capabilities that the server supports outside of the specification,
126        /// labeled using the Java package naming convention and stored as arbitrary JSON values.
127        #[serde(flatten)]
128        custom_capabilities: BTreeMap<String, JsonValue>,
129    }
130
131    impl Capabilities {
132        /// Creates empty `Capabilities`.
133        pub fn new() -> Self {
134            Default::default()
135        }
136
137        /// Returns the value of the given capability.
138        ///
139        /// Prefer to use the public fields of `Capabilities` where possible; this method is meant
140        /// to be used for unsupported capabilities only.
141        pub fn get(&self, capability: &str) -> Option<Cow<'_, JsonValue>> {
142            fn serialize<T: Serialize>(cap: &T) -> JsonValue {
143                to_json_value(cap).expect("capability serialization to succeed")
144            }
145
146            match capability {
147                "m.change_password" => Some(Cow::Owned(serialize(&self.change_password))),
148                "m.room_versions" => Some(Cow::Owned(serialize(&self.room_versions))),
149                "m.set_displayname" => Some(Cow::Owned(serialize(&self.set_displayname))),
150                "m.set_avatar_url" => Some(Cow::Owned(serialize(&self.set_avatar_url))),
151                "m.3pid_changes" => Some(Cow::Owned(serialize(&self.thirdparty_id_changes))),
152                "m.get_login_token" => Some(Cow::Owned(serialize(&self.get_login_token))),
153                _ => self.custom_capabilities.get(capability).map(Cow::Borrowed),
154            }
155        }
156
157        /// Sets a capability to the given value.
158        ///
159        /// Prefer to use the public fields of `Capabilities` where possible; this method is meant
160        /// to be used for unsupported capabilities only and does not allow setting
161        /// arbitrary data for supported ones.
162        pub fn set(&mut self, capability: &str, value: JsonValue) -> serde_json::Result<()> {
163            match capability {
164                "m.change_password" => self.change_password = from_json_value(value)?,
165                "m.room_versions" => self.room_versions = from_json_value(value)?,
166                "m.set_displayname" => self.set_displayname = from_json_value(value)?,
167                "m.set_avatar_url" => self.set_avatar_url = from_json_value(value)?,
168                "m.3pid_changes" => self.thirdparty_id_changes = from_json_value(value)?,
169                "m.get_login_token" => self.get_login_token = from_json_value(value)?,
170                _ => {
171                    self.custom_capabilities.insert(capability.to_owned(), value);
172                }
173            }
174
175            Ok(())
176        }
177    }
178
179    /// Information about the m.change_password capability
180    #[derive(Clone, Debug, Serialize, Deserialize)]
181    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
182    pub struct ChangePasswordCapability {
183        /// `true` if the user can change their password, `false` otherwise.
184        pub enabled: bool,
185    }
186
187    impl ChangePasswordCapability {
188        /// Creates a new `ChangePasswordCapability` with the given enabled flag.
189        pub fn new(enabled: bool) -> Self {
190            Self { enabled }
191        }
192
193        /// Returns whether all fields have their default value.
194        pub fn is_default(&self) -> bool {
195            self.enabled
196        }
197    }
198
199    impl Default for ChangePasswordCapability {
200        fn default() -> Self {
201            Self { enabled: true }
202        }
203    }
204
205    /// Information about the m.room_versions capability
206    #[derive(Clone, Debug, Serialize, Deserialize)]
207    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
208    pub struct RoomVersionsCapability {
209        /// The default room version the server is using for new rooms.
210        pub default: RoomVersionId,
211
212        /// A detailed description of the room versions the server supports.
213        pub available: BTreeMap<RoomVersionId, RoomVersionStability>,
214    }
215
216    impl RoomVersionsCapability {
217        /// Creates a new `RoomVersionsCapability` with the given default room version ID and room
218        /// version descriptions.
219        pub fn new(
220            default: RoomVersionId,
221            available: BTreeMap<RoomVersionId, RoomVersionStability>,
222        ) -> Self {
223            Self { default, available }
224        }
225
226        /// Returns whether all fields have their default value.
227        pub fn is_default(&self) -> bool {
228            self.default == RoomVersionId::V1
229                && self.available.len() == 1
230                && self
231                    .available
232                    .get(&RoomVersionId::V1)
233                    .map(|stability| *stability == RoomVersionStability::Stable)
234                    .unwrap_or(false)
235        }
236    }
237
238    impl Default for RoomVersionsCapability {
239        fn default() -> Self {
240            Self {
241                default: RoomVersionId::V1,
242                available: btreemap! { RoomVersionId::V1 => RoomVersionStability::Stable },
243            }
244        }
245    }
246
247    /// The stability of a room version.
248    #[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
249    #[derive(Clone, PartialEq, Eq, StringEnum)]
250    #[ruma_enum(rename_all = "lowercase")]
251    #[non_exhaustive]
252    pub enum RoomVersionStability {
253        /// Support for the given version is stable.
254        Stable,
255
256        /// Support for the given version is unstable.
257        Unstable,
258
259        #[doc(hidden)]
260        _Custom(PrivOwnedStr),
261    }
262
263    /// Information about the `m.set_displayname` capability
264    #[derive(Clone, Debug, Serialize, Deserialize)]
265    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
266    pub struct SetDisplayNameCapability {
267        /// `true` if the user can change their display name, `false` otherwise.
268        pub enabled: bool,
269    }
270
271    impl SetDisplayNameCapability {
272        /// Creates a new `SetDisplayNameCapability` with the given enabled flag.
273        pub fn new(enabled: bool) -> Self {
274            Self { enabled }
275        }
276
277        /// Returns whether all fields have their default value.
278        pub fn is_default(&self) -> bool {
279            self.enabled
280        }
281    }
282
283    impl Default for SetDisplayNameCapability {
284        fn default() -> Self {
285            Self { enabled: true }
286        }
287    }
288
289    /// Information about the `m.set_avatar_url` capability
290    #[derive(Clone, Debug, Serialize, Deserialize)]
291    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
292    pub struct SetAvatarUrlCapability {
293        /// `true` if the user can change their avatar, `false` otherwise.
294        pub enabled: bool,
295    }
296
297    impl SetAvatarUrlCapability {
298        /// Creates a new `SetAvatarUrlCapability` with the given enabled flag.
299        pub fn new(enabled: bool) -> Self {
300            Self { enabled }
301        }
302
303        /// Returns whether all fields have their default value.
304        pub fn is_default(&self) -> bool {
305            self.enabled
306        }
307    }
308
309    impl Default for SetAvatarUrlCapability {
310        fn default() -> Self {
311            Self { enabled: true }
312        }
313    }
314
315    /// Information about the `m.3pid_changes` capability
316    #[derive(Clone, Debug, Serialize, Deserialize)]
317    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
318    pub struct ThirdPartyIdChangesCapability {
319        /// `true` if the user can change the third-party identifiers associated with their
320        /// account, `false` otherwise.
321        pub enabled: bool,
322    }
323
324    impl ThirdPartyIdChangesCapability {
325        /// Creates a new `ThirdPartyIdChangesCapability` with the given enabled flag.
326        pub fn new(enabled: bool) -> Self {
327            Self { enabled }
328        }
329
330        /// Returns whether all fields have their default value.
331        pub fn is_default(&self) -> bool {
332            self.enabled
333        }
334    }
335
336    impl Default for ThirdPartyIdChangesCapability {
337        fn default() -> Self {
338            Self { enabled: true }
339        }
340    }
341
342    /// Information about the `m.get_login_token` capability.
343    #[derive(Clone, Debug, Default, Serialize, Deserialize)]
344    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
345    pub struct GetLoginTokenCapability {
346        /// Whether the user can request a login token.
347        pub enabled: bool,
348    }
349
350    impl GetLoginTokenCapability {
351        /// Creates a new `GetLoginTokenCapability` with the given enabled flag.
352        pub fn new(enabled: bool) -> Self {
353            Self { enabled }
354        }
355
356        /// Returns whether all fields have their default value.
357        pub fn is_default(&self) -> bool {
358            !self.enabled
359        }
360    }
361}