ruma_client_api/discovery/
get_capabilities.rs1pub mod v3 {
9 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(error = crate::Error)]
41 #[derive(Default)]
42 pub struct Request {}
43
44 #[response(error = crate::Error)]
46 pub struct Response {
47 pub capabilities: Capabilities,
49 }
50
51 impl Request {
52 pub fn new() -> Self {
54 Self {}
55 }
56 }
57
58 impl Response {
59 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 #[derive(Clone, Debug, Default, Serialize, Deserialize)]
73 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
74 #[allow(deprecated)]
75 pub struct Capabilities {
76 #[serde(
78 rename = "m.change_password",
79 default,
80 skip_serializing_if = "ChangePasswordCapability::is_default"
81 )]
82 pub change_password: ChangePasswordCapability,
83
84 #[serde(
86 rename = "m.room_versions",
87 default,
88 skip_serializing_if = "RoomVersionsCapability::is_default"
89 )]
90 pub room_versions: RoomVersionsCapability,
91
92 #[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 #[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 #[serde(
113 rename = "m.3pid_changes",
114 default,
115 skip_serializing_if = "ThirdPartyIdChangesCapability::is_default"
116 )]
117 pub thirdparty_id_changes: ThirdPartyIdChangesCapability,
118
119 #[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 #[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 #[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 #[serde(flatten)]
147 custom_capabilities: BTreeMap<String, JsonValue>,
148 }
149
150 impl Capabilities {
151 pub fn new() -> Self {
153 Default::default()
154 }
155
156 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 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 #[derive(Clone, Debug, Serialize, Deserialize)]
210 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
211 pub struct ChangePasswordCapability {
212 pub enabled: bool,
214 }
215
216 impl ChangePasswordCapability {
217 pub fn new(enabled: bool) -> Self {
219 Self { enabled }
220 }
221
222 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 #[derive(Clone, Debug, Serialize, Deserialize)]
236 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
237 pub struct RoomVersionsCapability {
238 pub default: RoomVersionId,
240
241 pub available: BTreeMap<RoomVersionId, RoomVersionStability>,
243 }
244
245 impl RoomVersionsCapability {
246 pub fn new(
249 default: RoomVersionId,
250 available: BTreeMap<RoomVersionId, RoomVersionStability>,
251 ) -> Self {
252 Self { default, available }
253 }
254
255 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 #[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 Stable,
284
285 Unstable,
287
288 #[doc(hidden)]
289 _Custom(PrivOwnedStr),
290 }
291
292 #[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 pub enabled: bool,
299 }
300
301 #[allow(deprecated)]
302 impl SetDisplayNameCapability {
303 pub fn new(enabled: bool) -> Self {
305 Self { enabled }
306 }
307
308 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 #[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 pub enabled: bool,
328 }
329
330 #[allow(deprecated)]
331 impl SetAvatarUrlCapability {
332 pub fn new(enabled: bool) -> Self {
334 Self { enabled }
335 }
336
337 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 #[derive(Clone, Debug, Serialize, Deserialize)]
352 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
353 pub struct ThirdPartyIdChangesCapability {
354 pub enabled: bool,
357 }
358
359 impl ThirdPartyIdChangesCapability {
360 pub fn new(enabled: bool) -> Self {
362 Self { enabled }
363 }
364
365 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 #[derive(Clone, Debug, Default, Serialize, Deserialize)]
379 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
380 pub struct GetLoginTokenCapability {
381 pub enabled: bool,
383 }
384
385 impl GetLoginTokenCapability {
386 pub fn new(enabled: bool) -> Self {
388 Self { enabled }
389 }
390
391 pub fn is_default(&self) -> bool {
393 !self.enabled
394 }
395 }
396
397 #[derive(Clone, Debug, Default, Serialize, Deserialize)]
399 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
400 pub struct ProfileFieldsCapability {
401 pub enabled: bool,
403
404 #[serde(skip_serializing_if = "Option::is_none")]
406 pub allowed: Option<Vec<ProfileFieldName>>,
407
408 #[serde(skip_serializing_if = "Option::is_none")]
412 pub disallowed: Option<Vec<ProfileFieldName>>,
413 }
414
415 impl ProfileFieldsCapability {
416 pub fn new(enabled: bool) -> Self {
418 Self { enabled, allowed: None, disallowed: None }
419 }
420
421 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 true
434 }
435 }
436 }
437
438 #[derive(Clone, Debug, Default, Serialize, Deserialize)]
440 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
441 pub struct ForgetForcedUponLeaveCapability {
442 pub enabled: bool,
447 }
448
449 impl ForgetForcedUponLeaveCapability {
450 pub fn new(enabled: bool) -> Self {
452 Self { enabled }
453 }
454
455 pub fn is_default(&self) -> bool {
457 !self.enabled
458 }
459 }
460}