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 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(error = crate::Error)]
43 #[derive(Default)]
44 pub struct Request {}
45
46 #[response(error = crate::Error)]
48 pub struct Response {
49 pub capabilities: Capabilities,
51 }
52
53 impl Request {
54 pub fn new() -> Self {
56 Self {}
57 }
58 }
59
60 impl Response {
61 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 #[derive(Clone, Debug, Default, Serialize, Deserialize)]
75 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
76 pub struct Capabilities {
77 #[serde(
79 rename = "m.change_password",
80 default,
81 skip_serializing_if = "ChangePasswordCapability::is_default"
82 )]
83 pub change_password: ChangePasswordCapability,
84
85 #[serde(
87 rename = "m.room_versions",
88 default,
89 skip_serializing_if = "RoomVersionsCapability::is_default"
90 )]
91 pub room_versions: RoomVersionsCapability,
92
93 #[serde(
95 rename = "m.set_displayname",
96 default,
97 skip_serializing_if = "SetDisplayNameCapability::is_default"
98 )]
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 pub set_avatar_url: SetAvatarUrlCapability,
108
109 #[serde(
112 rename = "m.3pid_changes",
113 default,
114 skip_serializing_if = "ThirdPartyIdChangesCapability::is_default"
115 )]
116 pub thirdparty_id_changes: ThirdPartyIdChangesCapability,
117
118 #[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 #[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 #[serde(flatten)]
139 custom_capabilities: BTreeMap<String, JsonValue>,
140 }
141
142 impl Capabilities {
143 pub fn new() -> Self {
145 Default::default()
146 }
147
148 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 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 #[derive(Clone, Debug, Serialize, Deserialize)]
192 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
193 pub struct ChangePasswordCapability {
194 pub enabled: bool,
196 }
197
198 impl ChangePasswordCapability {
199 pub fn new(enabled: bool) -> Self {
201 Self { enabled }
202 }
203
204 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 #[derive(Clone, Debug, Serialize, Deserialize)]
218 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
219 pub struct RoomVersionsCapability {
220 pub default: RoomVersionId,
222
223 pub available: BTreeMap<RoomVersionId, RoomVersionStability>,
225 }
226
227 impl RoomVersionsCapability {
228 pub fn new(
231 default: RoomVersionId,
232 available: BTreeMap<RoomVersionId, RoomVersionStability>,
233 ) -> Self {
234 Self { default, available }
235 }
236
237 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 #[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 Stable,
266
267 Unstable,
269
270 #[doc(hidden)]
271 _Custom(PrivOwnedStr),
272 }
273
274 #[derive(Clone, Debug, Serialize, Deserialize)]
276 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
277 pub struct SetDisplayNameCapability {
278 pub enabled: bool,
280 }
281
282 impl SetDisplayNameCapability {
283 pub fn new(enabled: bool) -> Self {
285 Self { enabled }
286 }
287
288 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 #[derive(Clone, Debug, Serialize, Deserialize)]
302 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
303 pub struct SetAvatarUrlCapability {
304 pub enabled: bool,
306 }
307
308 impl SetAvatarUrlCapability {
309 pub fn new(enabled: bool) -> Self {
311 Self { enabled }
312 }
313
314 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 #[derive(Clone, Debug, Serialize, Deserialize)]
328 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
329 pub struct ThirdPartyIdChangesCapability {
330 pub enabled: bool,
333 }
334
335 impl ThirdPartyIdChangesCapability {
336 pub fn new(enabled: bool) -> Self {
338 Self { enabled }
339 }
340
341 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 #[derive(Clone, Debug, Default, Serialize, Deserialize)]
355 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
356 pub struct GetLoginTokenCapability {
357 pub enabled: bool,
359 }
360
361 impl GetLoginTokenCapability {
362 pub fn new(enabled: bool) -> Self {
364 Self { enabled }
365 }
366
367 pub fn is_default(&self) -> bool {
369 !self.enabled
370 }
371 }
372
373 #[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 pub enabled: bool,
380
381 #[serde(skip_serializing_if = "Option::is_none")]
383 pub allowed: Option<Vec<ProfileFieldName>>,
384
385 #[serde(skip_serializing_if = "Option::is_none")]
389 pub disallowed: Option<Vec<ProfileFieldName>>,
390 }
391
392 #[cfg(feature = "unstable-msc4133")]
393 impl ProfileFieldsCapability {
394 pub fn new(enabled: bool) -> Self {
396 Self { enabled, allowed: None, disallowed: None }
397 }
398
399 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 true
412 }
413 }
414 }
415}