ruma_common/api/metadata.rs
1use std::{
2 cmp::Ordering,
3 collections::{BTreeMap, BTreeSet},
4 fmt::Display,
5 str::FromStr,
6};
7
8use bytes::BufMut;
9use http::Method;
10use ruma_macros::StringEnum;
11
12use super::{auth_scheme::AuthScheme, error::UnknownVersionError, path_builder::PathBuilder};
13use crate::{PrivOwnedStr, RoomVersionId, api::error::IntoHttpError, serde::slice_to_buf};
14
15/// Convenient constructor for [`Metadata`] implementation.
16///
17/// ## Definition
18///
19/// By default, `Metadata` is implemented on a type named `Request` that is in scope. This can be
20/// overridden by adding `@for MyType` at the beginning of the declaration.
21///
22/// The rest of the definition of the macro is made to look like a struct, with the following
23/// fields:
24///
25/// * `method` - The HTTP method to use for the endpoint. Its value must be one of the associated
26/// constants of [`http::Method`]. In most cases it should be one of `GET`, `POST`, `PUT` or
27/// `DELETE`.
28/// * `rate_limited` - Whether the endpoint should be rate-limited, according to the specification.
29/// Its value must be a `bool`.
30/// * `authentication` - The type of authentication that is required for the endpoint, according to
31/// the specification. The type must be in scope and implement [`AuthScheme`].
32///
33/// And either of the following fields to define the path(s) of the endpoint.
34///
35/// * `history` - The history of the paths of the endpoint. This should be used for endpoints from
36/// Matrix APIs that have a `/versions` endpoint that returns a list a [`MatrixVersion`]s and
37/// possibly features, like the Client-Server API or the Identity Service API. However, a few
38/// endpoints from those APIs shouldn't use this field because they cannot be versioned, like the
39/// `/versions` or the `/.well-known` endpoints.
40///
41/// Its definition is made to look like match arms and must include at least one arm. The match
42/// arms accept the following syntax:
43///
44/// * `unstable => "unstable/endpoint/path/{variable}"` - An unstable version of the endpoint as
45/// defined in the MSC that adds it, if the MSC does **NOT** define an unstable feature in the
46/// `unstable_features` field of the client-server API's `/versions` endpoint.
47/// * `unstable("org.bar.unstable_feature") => "unstable/endpoint/path/{variable}"` - An unstable
48/// version of the endpoint as defined in the MSC that adds it, if the MSC defines an unstable
49/// feature in the `unstable_features` field of the client-server API's `/versions` endpoint.
50/// * `1.0 | stable("org.bar.feature.stable") => "stable/endpoint/path/{variable}"` - A stable
51/// version of the endpoint as defined in an MSC or the Matrix specification. The match arm can
52/// be a Matrix version, a stable feature, or both separated by `|`.
53///
54/// A stable feature can be defined in an MSC alongside an unstable feature, and can be found in
55/// the `unstable_features` field of the client-server API's `/versions` endpoint. It is meant
56/// to be used by homeservers if they want to declare stable support for a feature before they
57/// can declare support for a whole Matrix version that supports it.
58///
59/// * `1.2 => deprecated` - The Matrix version that deprecated the endpoint, if any. It must be
60/// preceded by a match arm with a stable path and a different Matrix version.
61/// * `1.3 => removed` - The Matrix version that removed the endpoint, if any. It must be preceded
62/// by a match arm with a deprecation and a different Matrix version.
63///
64/// A Matrix version is a `float` representation of the version that looks like `major.minor`.
65/// It must match one of the variants of [`MatrixVersion`]. For example `1.0` matches
66/// [`MatrixVersion::V1_0`], `1.1` matches [`MatrixVersion::V1_1`], etc.
67///
68/// It is expected that the match arms are ordered by descending age. Usually the older unstable
69/// paths would be before the newer unstable paths, then we would find the stable paths, and
70/// finally the deprecation and removal.
71///
72/// The following checks occur at compile time:
73///
74/// * All unstable and stable paths contain the same variables (or lack thereof).
75/// * Matrix versions in match arms are all different and in ascending order.
76///
77/// This field is represented as the [`VersionHistory`](super::path_builder::VersionHistory) type
78/// in the generated implementation.
79/// * `path` - The only path of the endpoint. This should be used for endpoints from Matrix APIs
80/// that do NOT have a `/versions` endpoint that returns a list a [`MatrixVersion`]s, like the
81/// Server-Server API or the Appservice API. It should also be used for endpoints that cannot be
82/// versioned, like the `/versions` or the `/.well-known` endpoints.
83///
84/// Its value must be a static string representing the path, like `"endpoint/path/{variable}"`.
85///
86/// This field is represented as the [`SinglePath`](super::path_builder::SinglePath) type in the
87/// generated implementation.
88///
89/// ## Example
90///
91/// ```
92/// use ruma_common::{
93/// api::auth_scheme::{AccessToken, NoAuthentication},
94/// metadata,
95/// };
96///
97/// /// A Request with a path version history.
98/// pub struct Request {
99/// body: Vec<u8>,
100/// }
101///
102/// metadata! {
103/// method: GET,
104/// rate_limited: true,
105/// authentication: AccessToken,
106///
107/// history: {
108/// unstable => "/_matrix/unstable/org.bar.msc9000/baz",
109/// unstable("org.bar.msc9000.v1") => "/_matrix/unstable/org.bar.msc9000.v1/qux",
110/// 1.0 | stable("org.bar.msc9000.stable") => "/_matrix/media/r0/qux",
111/// 1.1 => "/_matrix/media/v3/qux",
112/// 1.2 => deprecated,
113/// 1.3 => removed,
114/// }
115/// };
116///
117/// /// A request with a single path.
118/// pub struct MySinglePathRequest {
119/// body: Vec<u8>,
120/// }
121///
122/// metadata! {
123/// @for MySinglePathRequest,
124///
125/// method: GET,
126/// rate_limited: false,
127/// authentication: NoAuthentication,
128/// path: "/_matrix/key/query",
129/// };
130/// ```
131#[doc(hidden)]
132#[macro_export]
133macro_rules! metadata {
134 ( @for $request_type:ty, $( $field:ident: $rhs:tt ),+ $(,)? ) => {
135 #[allow(deprecated)]
136 impl $crate::api::Metadata for $request_type {
137 $( $crate::metadata!(@field $field: $rhs); )+
138 }
139 };
140
141 ( $( $field:ident: $rhs:tt ),+ $(,)? ) => {
142 $crate::metadata!{ @for Request, $( $field: $rhs),+ }
143 };
144
145 ( @field method: $method:ident ) => {
146 const METHOD: $crate::exports::http::Method = $crate::exports::http::Method::$method;
147 };
148
149 ( @field rate_limited: $rate_limited:literal ) => { const RATE_LIMITED: bool = $rate_limited; };
150
151 ( @field authentication: $scheme:path ) => {
152 type Authentication = $scheme;
153 };
154
155 ( @field path: $path:literal ) => {
156 type PathBuilder = $crate::api::path_builder::SinglePath;
157 const PATH_BUILDER: $crate::api::path_builder::SinglePath = $crate::api::path_builder::SinglePath::new($path);
158 };
159
160 ( @field history: {
161 $( unstable $(($unstable_feature:literal))? => $unstable_path:literal, )*
162 $( stable ($stable_feature_only:literal) => $stable_feature_path:literal, )?
163 $( $( $version:literal $(| stable ($stable_feature:literal))? => $rhs:tt, )+ )?
164 } ) => {
165 $crate::metadata! {
166 @history_impl
167 [ $( $unstable_path $(= $unstable_feature)? ),* ]
168 $( stable ($stable_feature_only) => $stable_feature_path, )?
169 // Flip left and right to avoid macro parsing ambiguities
170 $( $( $rhs = $version $(| stable ($stable_feature))? ),+ )?
171 }
172 };
173
174 ( @history_impl
175 [ $( $unstable_path:literal $(= $unstable_feature:literal)? ),* ]
176 $( stable ($stable_feature_only:literal) => $stable_feature_path:literal, )?
177 $(
178 $( $stable_path:literal = $version:literal $(| stable ($stable_feature:literal))? ),+
179 $(,
180 deprecated = $deprecated_version:literal
181 $(, removed = $removed_version:literal )?
182 )?
183 )?
184 ) => {
185 type PathBuilder = $crate::api::path_builder::VersionHistory;
186 const PATH_BUILDER: $crate::api::path_builder::VersionHistory = $crate::api::path_builder::VersionHistory::new(
187 &[ $(($crate::metadata!(@optional_feature $($unstable_feature)?), $unstable_path)),* ],
188 &[
189 $((
190 $crate::metadata!(@stable_path_selector stable($stable_feature_only)),
191 $stable_feature_path
192 ),)?
193 $($((
194 $crate::metadata!(@stable_path_selector $version $(| stable($stable_feature))?),
195 $stable_path
196 )),+)?
197 ],
198 $crate::metadata!(@optional_version $($( $deprecated_version )?)?),
199 $crate::metadata!(@optional_version $($($( $removed_version )?)?)?),
200 );
201 };
202
203 ( @optional_feature ) => { None };
204 ( @optional_feature $feature:literal ) => { Some($feature) };
205 ( @stable_path_selector stable($feature:literal)) => {
206 $crate::api::path_builder::StablePathSelector::Feature($feature)
207 };
208 ( @stable_path_selector $version:literal | stable($feature:literal)) => {
209 $crate::api::path_builder::StablePathSelector::FeatureAndVersion {
210 feature: $feature,
211 version: $crate::api::MatrixVersion::from_lit(stringify!($version)),
212 }
213 };
214 ( @stable_path_selector $version:literal) => {
215 $crate::api::path_builder::StablePathSelector::Version(
216 $crate::api::MatrixVersion::from_lit(stringify!($version))
217 )
218 };
219 ( @optional_version ) => { None };
220 ( @optional_version $version:literal ) => { Some($crate::api::MatrixVersion::from_lit(stringify!($version))) }
221}
222
223/// Metadata about an API endpoint.
224pub trait Metadata: Sized {
225 /// The HTTP method used by this endpoint.
226 const METHOD: Method;
227
228 /// Whether or not this endpoint is rate limited by the server.
229 const RATE_LIMITED: bool;
230
231 /// What authentication scheme the server uses for this endpoint.
232 type Authentication: AuthScheme;
233
234 /// The type used to build an endpoint's path.
235 type PathBuilder: PathBuilder;
236
237 /// All info pertaining to an endpoint's path.
238 const PATH_BUILDER: Self::PathBuilder;
239
240 /// Returns an empty request body for this Matrix request.
241 ///
242 /// For `GET` requests, it returns an entirely empty buffer, for others it returns an empty JSON
243 /// object (`{}`).
244 fn empty_request_body<B>() -> B
245 where
246 B: Default + BufMut,
247 {
248 if Self::METHOD == Method::GET { Default::default() } else { slice_to_buf(b"{}") }
249 }
250
251 /// Generate the endpoint URL for this endpoint.
252 fn make_endpoint_url(
253 path_builder_input: <Self::PathBuilder as PathBuilder>::Input<'_>,
254 base_url: &str,
255 path_args: &[&dyn Display],
256 query_string: &str,
257 ) -> Result<String, IntoHttpError> {
258 Self::PATH_BUILDER.make_endpoint_url(path_builder_input, base_url, path_args, query_string)
259 }
260
261 /// The list of path parameters in the metadata.
262 ///
263 /// Used for `#[test]`s generated by the API macros.
264 #[doc(hidden)]
265 fn _path_parameters() -> Vec<&'static str> {
266 Self::PATH_BUILDER._path_parameters()
267 }
268}
269
270/// The Matrix versions Ruma currently understands to exist.
271///
272/// Matrix, since fall 2021, has a quarterly release schedule, using a global `vX.Y` versioning
273/// scheme. Usually `Y` is bumped for new backwards compatible changes, but `X` can be bumped
274/// instead when a large number of `Y` changes feel deserving of a major version increase.
275///
276/// Every new version denotes stable support for endpoints in a *relatively* backwards-compatible
277/// manner.
278///
279/// Matrix has a deprecation policy, read more about it here: <https://spec.matrix.org/latest/#deprecation-policy>.
280///
281/// Ruma keeps track of when endpoints are added, deprecated, and removed. It'll automatically
282/// select the right endpoint stability variation to use depending on which Matrix versions you
283/// pass to [`try_into_http_request`](super::OutgoingRequest::try_into_http_request), see its
284/// respective documentation for more information.
285///
286/// The `PartialOrd` and `Ord` implementations of this type sort the variants by release date. A
287/// newer release is greater than an older release.
288///
289/// `MatrixVersion::is_superset_of()` is used to keep track of compatibility between versions.
290#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
291#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
292pub enum MatrixVersion {
293 /// Matrix 1.0 was a release prior to the global versioning system and does not correspond to a
294 /// version of the Matrix specification.
295 ///
296 /// It matches the following per-API versions:
297 ///
298 /// * Client-Server API: r0.5.0 to r0.6.1
299 /// * Identity Service API: r0.2.0 to r0.3.0
300 ///
301 /// The other APIs are not supported because they do not have a `GET /versions` endpoint.
302 ///
303 /// See <https://spec.matrix.org/latest/#legacy-versioning>.
304 V1_0,
305
306 /// Version 1.1 of the Matrix specification, released in Q4 2021.
307 ///
308 /// See <https://spec.matrix.org/v1.1/>.
309 V1_1,
310
311 /// Version 1.2 of the Matrix specification, released in Q1 2022.
312 ///
313 /// See <https://spec.matrix.org/v1.2/>.
314 V1_2,
315
316 /// Version 1.3 of the Matrix specification, released in Q2 2022.
317 ///
318 /// See <https://spec.matrix.org/v1.3/>.
319 V1_3,
320
321 /// Version 1.4 of the Matrix specification, released in Q3 2022.
322 ///
323 /// See <https://spec.matrix.org/v1.4/>.
324 V1_4,
325
326 /// Version 1.5 of the Matrix specification, released in Q4 2022.
327 ///
328 /// See <https://spec.matrix.org/v1.5/>.
329 V1_5,
330
331 /// Version 1.6 of the Matrix specification, released in Q1 2023.
332 ///
333 /// See <https://spec.matrix.org/v1.6/>.
334 V1_6,
335
336 /// Version 1.7 of the Matrix specification, released in Q2 2023.
337 ///
338 /// See <https://spec.matrix.org/v1.7/>.
339 V1_7,
340
341 /// Version 1.8 of the Matrix specification, released in Q3 2023.
342 ///
343 /// See <https://spec.matrix.org/v1.8/>.
344 V1_8,
345
346 /// Version 1.9 of the Matrix specification, released in Q4 2023.
347 ///
348 /// See <https://spec.matrix.org/v1.9/>.
349 V1_9,
350
351 /// Version 1.10 of the Matrix specification, released in Q1 2024.
352 ///
353 /// See <https://spec.matrix.org/v1.10/>.
354 V1_10,
355
356 /// Version 1.11 of the Matrix specification, released in Q2 2024.
357 ///
358 /// See <https://spec.matrix.org/v1.11/>.
359 V1_11,
360
361 /// Version 1.12 of the Matrix specification, released in Q3 2024.
362 ///
363 /// See <https://spec.matrix.org/v1.12/>.
364 V1_12,
365
366 /// Version 1.13 of the Matrix specification, released in Q4 2024.
367 ///
368 /// See <https://spec.matrix.org/v1.13/>.
369 V1_13,
370
371 /// Version 1.14 of the Matrix specification, released in Q1 2025.
372 ///
373 /// See <https://spec.matrix.org/v1.14/>.
374 V1_14,
375
376 /// Version 1.15 of the Matrix specification, released in Q2 2025.
377 ///
378 /// See <https://spec.matrix.org/v1.15/>.
379 V1_15,
380
381 /// Version 1.16 of the Matrix specification, released in Q3 2025.
382 ///
383 /// See <https://spec.matrix.org/v1.16/>.
384 V1_16,
385}
386
387impl TryFrom<&str> for MatrixVersion {
388 type Error = UnknownVersionError;
389
390 fn try_from(value: &str) -> Result<MatrixVersion, Self::Error> {
391 use MatrixVersion::*;
392
393 Ok(match value {
394 // Identity service API versions between Matrix 1.0 and 1.1.
395 // They might match older client-server API versions but that should not be a problem in practice.
396 "r0.2.0" | "r0.2.1" | "r0.3.0" |
397 // Client-server API versions between Matrix 1.0 and 1.1.
398 "r0.5.0" | "r0.6.0" | "r0.6.1" => V1_0,
399 "v1.1" => V1_1,
400 "v1.2" => V1_2,
401 "v1.3" => V1_3,
402 "v1.4" => V1_4,
403 "v1.5" => V1_5,
404 "v1.6" => V1_6,
405 "v1.7" => V1_7,
406 "v1.8" => V1_8,
407 "v1.9" => V1_9,
408 "v1.10" => V1_10,
409 "v1.11" => V1_11,
410 "v1.12" => V1_12,
411 "v1.13" => V1_13,
412 "v1.14" => V1_14,
413 "v1.15" => V1_15,
414 "v1.16" => V1_16,
415 _ => return Err(UnknownVersionError),
416 })
417 }
418}
419
420impl FromStr for MatrixVersion {
421 type Err = UnknownVersionError;
422
423 fn from_str(s: &str) -> Result<Self, Self::Err> {
424 Self::try_from(s)
425 }
426}
427
428impl MatrixVersion {
429 /// Checks whether a version is compatible with another.
430 ///
431 /// Currently, all versions of Matrix are considered backwards compatible with all the previous
432 /// versions, so this is equivalent to `self >= other`. This behaviour may change in the future,
433 /// if a new release is considered to be breaking compatibility with the previous ones.
434 ///
435 /// > ⚠ Matrix has a deprecation policy, and Matrix versioning is not as straightforward as this
436 /// > function makes it out to be. This function only exists to prune breaking changes between
437 /// > versions, and versions too new for `self`.
438 pub fn is_superset_of(self, other: Self) -> bool {
439 self >= other
440 }
441
442 /// Get a string representation of this Matrix version.
443 ///
444 /// This is the string that can be found in the response to one of the `GET /versions`
445 /// endpoints. Parsing this string will give the same variant.
446 ///
447 /// Returns `None` for [`MatrixVersion::V1_0`] because it can match several per-API versions.
448 pub const fn as_str(self) -> Option<&'static str> {
449 let string = match self {
450 MatrixVersion::V1_0 => return None,
451 MatrixVersion::V1_1 => "v1.1",
452 MatrixVersion::V1_2 => "v1.2",
453 MatrixVersion::V1_3 => "v1.3",
454 MatrixVersion::V1_4 => "v1.4",
455 MatrixVersion::V1_5 => "v1.5",
456 MatrixVersion::V1_6 => "v1.6",
457 MatrixVersion::V1_7 => "v1.7",
458 MatrixVersion::V1_8 => "v1.8",
459 MatrixVersion::V1_9 => "v1.9",
460 MatrixVersion::V1_10 => "v1.10",
461 MatrixVersion::V1_11 => "v1.11",
462 MatrixVersion::V1_12 => "v1.12",
463 MatrixVersion::V1_13 => "v1.13",
464 MatrixVersion::V1_14 => "v1.14",
465 MatrixVersion::V1_15 => "v1.15",
466 MatrixVersion::V1_16 => "v1.16",
467 };
468
469 Some(string)
470 }
471
472 /// Decompose the Matrix version into its major and minor number.
473 const fn into_parts(self) -> (u8, u8) {
474 match self {
475 MatrixVersion::V1_0 => (1, 0),
476 MatrixVersion::V1_1 => (1, 1),
477 MatrixVersion::V1_2 => (1, 2),
478 MatrixVersion::V1_3 => (1, 3),
479 MatrixVersion::V1_4 => (1, 4),
480 MatrixVersion::V1_5 => (1, 5),
481 MatrixVersion::V1_6 => (1, 6),
482 MatrixVersion::V1_7 => (1, 7),
483 MatrixVersion::V1_8 => (1, 8),
484 MatrixVersion::V1_9 => (1, 9),
485 MatrixVersion::V1_10 => (1, 10),
486 MatrixVersion::V1_11 => (1, 11),
487 MatrixVersion::V1_12 => (1, 12),
488 MatrixVersion::V1_13 => (1, 13),
489 MatrixVersion::V1_14 => (1, 14),
490 MatrixVersion::V1_15 => (1, 15),
491 MatrixVersion::V1_16 => (1, 16),
492 }
493 }
494
495 /// Try to turn a pair of (major, minor) version components back into a `MatrixVersion`.
496 const fn from_parts(major: u8, minor: u8) -> Result<Self, UnknownVersionError> {
497 match (major, minor) {
498 (1, 0) => Ok(MatrixVersion::V1_0),
499 (1, 1) => Ok(MatrixVersion::V1_1),
500 (1, 2) => Ok(MatrixVersion::V1_2),
501 (1, 3) => Ok(MatrixVersion::V1_3),
502 (1, 4) => Ok(MatrixVersion::V1_4),
503 (1, 5) => Ok(MatrixVersion::V1_5),
504 (1, 6) => Ok(MatrixVersion::V1_6),
505 (1, 7) => Ok(MatrixVersion::V1_7),
506 (1, 8) => Ok(MatrixVersion::V1_8),
507 (1, 9) => Ok(MatrixVersion::V1_9),
508 (1, 10) => Ok(MatrixVersion::V1_10),
509 (1, 11) => Ok(MatrixVersion::V1_11),
510 (1, 12) => Ok(MatrixVersion::V1_12),
511 (1, 13) => Ok(MatrixVersion::V1_13),
512 (1, 14) => Ok(MatrixVersion::V1_14),
513 (1, 15) => Ok(MatrixVersion::V1_15),
514 (1, 16) => Ok(MatrixVersion::V1_16),
515 _ => Err(UnknownVersionError),
516 }
517 }
518
519 /// Constructor for use by the `metadata!` macro.
520 ///
521 /// Accepts string literals and parses them.
522 #[doc(hidden)]
523 pub const fn from_lit(lit: &'static str) -> Self {
524 use konst::{option, primitive::parse_u8, result, string};
525
526 let major: u8;
527 let minor: u8;
528
529 let mut lit_iter = string::split(lit, ".").next();
530
531 {
532 let (checked_first, checked_split) = option::unwrap!(lit_iter); // First iteration always succeeds
533
534 major = result::unwrap_or_else!(parse_u8(checked_first), |_| panic!(
535 "major version is not a valid number"
536 ));
537
538 lit_iter = checked_split.next();
539 }
540
541 match lit_iter {
542 Some((checked_second, checked_split)) => {
543 minor = result::unwrap_or_else!(parse_u8(checked_second), |_| panic!(
544 "minor version is not a valid number"
545 ));
546
547 lit_iter = checked_split.next();
548 }
549 None => panic!("could not find dot to denote second number"),
550 }
551
552 if lit_iter.is_some() {
553 panic!("version literal contains more than one dot")
554 }
555
556 result::unwrap_or_else!(Self::from_parts(major, minor), |_| panic!(
557 "not a valid version literal"
558 ))
559 }
560
561 // Internal function to do ordering in const-fn contexts
562 pub(super) const fn const_ord(&self, other: &Self) -> Ordering {
563 let self_parts = self.into_parts();
564 let other_parts = other.into_parts();
565
566 use konst::primitive::cmp::cmp_u8;
567
568 let major_ord = cmp_u8(self_parts.0, other_parts.0);
569 if major_ord.is_ne() { major_ord } else { cmp_u8(self_parts.1, other_parts.1) }
570 }
571
572 // Internal function to check if this version is the legacy (v1.0) version in const-fn contexts
573 pub(super) const fn is_legacy(&self) -> bool {
574 let self_parts = self.into_parts();
575
576 use konst::primitive::cmp::cmp_u8;
577
578 cmp_u8(self_parts.0, 1).is_eq() && cmp_u8(self_parts.1, 0).is_eq()
579 }
580
581 /// Get the default [`RoomVersionId`] for this `MatrixVersion`.
582 pub fn default_room_version(&self) -> RoomVersionId {
583 match self {
584 // <https://spec.matrix.org/historical/index.html#complete-list-of-room-versions>
585 MatrixVersion::V1_0
586 // <https://spec.matrix.org/v1.1/rooms/#complete-list-of-room-versions>
587 | MatrixVersion::V1_1
588 // <https://spec.matrix.org/v1.2/rooms/#complete-list-of-room-versions>
589 | MatrixVersion::V1_2 => RoomVersionId::V6,
590 // <https://spec.matrix.org/v1.3/rooms/#complete-list-of-room-versions>
591 MatrixVersion::V1_3
592 // <https://spec.matrix.org/v1.4/rooms/#complete-list-of-room-versions>
593 | MatrixVersion::V1_4
594 // <https://spec.matrix.org/v1.5/rooms/#complete-list-of-room-versions>
595 | MatrixVersion::V1_5 => RoomVersionId::V9,
596 // <https://spec.matrix.org/v1.6/rooms/#complete-list-of-room-versions>
597 MatrixVersion::V1_6
598 // <https://spec.matrix.org/v1.7/rooms/#complete-list-of-room-versions>
599 | MatrixVersion::V1_7
600 // <https://spec.matrix.org/v1.8/rooms/#complete-list-of-room-versions>
601 | MatrixVersion::V1_8
602 // <https://spec.matrix.org/v1.9/rooms/#complete-list-of-room-versions>
603 | MatrixVersion::V1_9
604 // <https://spec.matrix.org/v1.10/rooms/#complete-list-of-room-versions>
605 | MatrixVersion::V1_10
606 // <https://spec.matrix.org/v1.11/rooms/#complete-list-of-room-versions>
607 | MatrixVersion::V1_11
608 // <https://spec.matrix.org/v1.12/rooms/#complete-list-of-room-versions>
609 | MatrixVersion::V1_12
610 // <https://spec.matrix.org/v1.13/rooms/#complete-list-of-room-versions>
611 | MatrixVersion::V1_13 => RoomVersionId::V10,
612 // <https://spec.matrix.org/v1.14/rooms/#complete-list-of-room-versions>
613 | MatrixVersion::V1_14
614 // <https://spec.matrix.org/v1.15/rooms/#complete-list-of-room-versions>
615 | MatrixVersion::V1_15 => RoomVersionId::V11,
616 // <https://spec.matrix.org/v1.16/rooms/#complete-list-of-room-versions>
617 MatrixVersion::V1_16 => RoomVersionId::V12,
618 }
619 }
620}
621
622/// The list of Matrix versions and features supported by a homeserver.
623#[derive(Debug, Clone)]
624#[allow(clippy::exhaustive_structs)]
625pub struct SupportedVersions {
626 /// The Matrix versions that are supported by the homeserver.
627 ///
628 /// This set contains only known versions.
629 pub versions: BTreeSet<MatrixVersion>,
630
631 /// The features that are supported by the homeserver.
632 ///
633 /// This matches the `unstable_features` field of the `/versions` endpoint, without the boolean
634 /// value.
635 pub features: BTreeSet<FeatureFlag>,
636}
637
638impl SupportedVersions {
639 /// Construct a `SupportedVersions` from the parts of a `/versions` response.
640 ///
641 /// Matrix versions that can't be parsed to a `MatrixVersion`, and features with the boolean
642 /// value set to `false` are discarded.
643 pub fn from_parts(versions: &[String], unstable_features: &BTreeMap<String, bool>) -> Self {
644 Self {
645 versions: versions.iter().flat_map(|s| s.parse::<MatrixVersion>()).collect(),
646 features: unstable_features
647 .iter()
648 .filter(|(_, enabled)| **enabled)
649 .map(|(feature, _)| feature.as_str().into())
650 .collect(),
651 }
652 }
653}
654
655/// The Matrix features supported by Ruma.
656///
657/// Features that are not behind a cargo feature are features that are part of the Matrix
658/// specification and that Ruma still supports, like the unstable version of an endpoint or a stable
659/// feature. Features behind a cargo feature are only supported when this feature is enabled.
660#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
661#[derive(Clone, StringEnum, Hash)]
662#[non_exhaustive]
663pub enum FeatureFlag {
664 /// `fi.mau.msc2246` ([MSC])
665 ///
666 /// Asynchronous media uploads.
667 ///
668 /// [MSC]: https://github.com/matrix-org/matrix-spec-proposals/pull/2246
669 #[ruma_enum(rename = "fi.mau.msc2246")]
670 Msc2246,
671
672 /// `org.matrix.msc2432` ([MSC])
673 ///
674 /// Updated semantics for publishing room aliases.
675 ///
676 /// [MSC]: https://github.com/matrix-org/matrix-spec-proposals/pull/2432
677 #[ruma_enum(rename = "org.matrix.msc2432")]
678 Msc2432,
679
680 /// `fi.mau.msc2659` ([MSC])
681 ///
682 /// Application service ping endpoint.
683 ///
684 /// [MSC]: https://github.com/matrix-org/matrix-spec-proposals/pull/2659
685 #[ruma_enum(rename = "fi.mau.msc2659")]
686 Msc2659,
687
688 /// `fi.mau.msc2659` ([MSC])
689 ///
690 /// Stable version of the application service ping endpoint.
691 ///
692 /// [MSC]: https://github.com/matrix-org/matrix-spec-proposals/pull/2659
693 #[ruma_enum(rename = "fi.mau.msc2659.stable")]
694 Msc2659Stable,
695
696 /// `uk.half-shot.msc2666.query_mutual_rooms` ([MSC])
697 ///
698 /// Get rooms in common with another user.
699 ///
700 /// [MSC]: https://github.com/matrix-org/matrix-spec-proposals/pull/2666
701 #[cfg(feature = "unstable-msc2666")]
702 #[ruma_enum(rename = "uk.half-shot.msc2666.query_mutual_rooms")]
703 Msc2666,
704
705 /// `org.matrix.msc3030` ([MSC])
706 ///
707 /// Jump to date API endpoint.
708 ///
709 /// [MSC]: https://github.com/matrix-org/matrix-spec-proposals/pull/3030
710 #[ruma_enum(rename = "org.matrix.msc3030")]
711 Msc3030,
712
713 /// `org.matrix.msc3882` ([MSC])
714 ///
715 /// Allow an existing session to sign in a new session.
716 ///
717 /// [MSC]: https://github.com/matrix-org/matrix-spec-proposals/pull/3882
718 #[ruma_enum(rename = "org.matrix.msc3882")]
719 Msc3882,
720
721 /// `org.matrix.msc3916` ([MSC])
722 ///
723 /// Authentication for media.
724 ///
725 /// [MSC]: https://github.com/matrix-org/matrix-spec-proposals/pull/3916
726 #[ruma_enum(rename = "org.matrix.msc3916")]
727 Msc3916,
728
729 /// `org.matrix.msc3916.stable` ([MSC])
730 ///
731 /// Stable version of authentication for media.
732 ///
733 /// [MSC]: https://github.com/matrix-org/matrix-spec-proposals/pull/3916
734 #[ruma_enum(rename = "org.matrix.msc3916.stable")]
735 Msc3916Stable,
736
737 /// `org.matrix.msc4108` ([MSC])
738 ///
739 /// Mechanism to allow OIDC sign in and E2EE set up via QR code.
740 ///
741 /// [MSC]: https://github.com/matrix-org/matrix-spec-proposals/pull/4108
742 #[cfg(feature = "unstable-msc4108")]
743 #[ruma_enum(rename = "org.matrix.msc4108")]
744 Msc4108,
745
746 /// `org.matrix.msc4140` ([MSC])
747 ///
748 /// Delayed events.
749 ///
750 /// [MSC]: https://github.com/matrix-org/matrix-spec-proposals/pull/4140
751 #[cfg(feature = "unstable-msc4140")]
752 #[ruma_enum(rename = "org.matrix.msc4140")]
753 Msc4140,
754
755 /// `org.matrix.simplified_msc3575` ([MSC])
756 ///
757 /// Simplified Sliding Sync.
758 ///
759 /// [MSC]: https://github.com/matrix-org/matrix-spec-proposals/pull/4186
760 #[cfg(feature = "unstable-msc4186")]
761 #[ruma_enum(rename = "org.matrix.simplified_msc3575")]
762 Msc4186,
763
764 #[doc(hidden)]
765 _Custom(PrivOwnedStr),
766}