api: Add MatrixVersion enum

Co-authored-by: Jonas Platte <jplatte@users.noreply.github.com>
This commit is contained in:
Jonathan de Jong 2022-02-04 12:32:12 +01:00 committed by GitHub
parent 9c4bf00f79
commit 19fd35d066
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 159 additions and 3 deletions

View File

@ -18,7 +18,11 @@
#[cfg(not(all(feature = "client", feature = "server")))] #[cfg(not(all(feature = "client", feature = "server")))]
compile_error!("ruma_api's Cargo features only exist as a workaround are not meant to be disabled"); compile_error!("ruma_api's Cargo features only exist as a workaround are not meant to be disabled");
use std::{convert::TryInto as _, error::Error as StdError}; use std::{
convert::{TryFrom, TryInto as _},
error::Error as StdError,
fmt::{self, Display},
};
use bytes::BufMut; use bytes::BufMut;
use http::Method; use http::Method;
@ -421,3 +425,140 @@ pub struct Metadata {
/// What authentication scheme the server uses for this endpoint. /// What authentication scheme the server uses for this endpoint.
pub authentication: AuthScheme, pub authentication: AuthScheme,
} }
/// The Matrix versions Ruma currently understands to exist.
///
/// Matrix, since fall 2021, has a quarterly release schedule, using a global `vX.Y` versioning
/// scheme.
///
/// Every new minor version denotes stable support for endpoints in a *relatively*
/// backwards-compatible manner.
///
/// Matrix has a deprecation policy, read more about it here: <https://spec.matrix.org/v1.2/#deprecation-policy>.
// TODO add the following once `EndpointPath` and added/deprecated/removed macros are in place;
// Ruma keeps track of when endpoints are added, deprecated, and removed. It'll automatically
// select the right endpoint stability variation to use depending on which Matrix version you pass
// it with [`EndpointPath`], see its respective documentation for more.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub enum MatrixVersion {
/// Version 1.0 of the Matrix specification.
///
/// Retroactively defined as <https://spec.matrix.org/v1.1/#legacy-versioning>.
V1_0,
/// Version 1.1 of the Matrix specification, released in Q4 2021.
///
/// See <https://spec.matrix.org/v1.1/>.
V1_1,
/// Version 1.2 of the Matrix specification, released in Q1 2022.
///
/// See <https://spec.matrix.org/v1.2/>.
V1_2,
}
/// An error that happens when Ruma cannot understand a Matrix version.
#[derive(Debug)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct UnknownVersionError;
impl Display for UnknownVersionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Version string was unknown.")
}
}
impl StdError for UnknownVersionError {}
impl TryFrom<&str> for MatrixVersion {
type Error = UnknownVersionError;
fn try_from(value: &str) -> Result<MatrixVersion, Self::Error> {
use MatrixVersion::*;
Ok(match value {
// FIXME: these are likely not entirely correct; https://github.com/ruma/ruma/issues/852
"v1.0" |
// Additional definitions according to https://spec.matrix.org/v1.2/#legacy-versioning
"r0.5.0" | "r0.6.0" | "r0.6.1" => V1_0,
"v1.1" => V1_1,
"v1.2" => V1_2,
_ => return Err(UnknownVersionError),
})
}
}
impl Display for MatrixVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let r = self.repr();
f.write_str(&format!("v{}.{}", r.major, r.minor))
}
}
// Internal-only structure to abstract away version representations into Major-Minor bits.
//
// This is not represented on MatrixVersion due to the major footguns it exposes,
// maybe in the future this'll be merged into it, but not now.
#[derive(PartialEq)]
struct VersionRepr {
major: u8,
minor: u8,
}
impl VersionRepr {
fn new(major: u8, minor: u8) -> Self {
VersionRepr { major, minor }
}
}
// We don't expose this on MatrixVersion due to the subtleties of non-total ordering semantics
// the Matrix versions have; we cannot guarantee ordering between major versions, and only between
// minor versions of the same major one.
//
// This means that V2_0 > V1_0 returns false, and V2_0 < V1_0 too.
//
// This sort of behavior has to be pre-emptively known by the programmer, which is the definition of
// a gotcha/footgun.
//
// As such, we're not including it in the public API (only using it via `MatrixVersion::compatible`)
// until we find a way to introduce it in a way that exposes the ambiguities to the programmer.
impl PartialOrd for VersionRepr {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
if self.major != other.major {
// Ordering between major versions is non-total.
return None;
}
self.minor.partial_cmp(&other.minor)
}
}
impl MatrixVersion {
/// Checks wether a version is compatible with another.
///
/// A is compatible with B as long as B is equal or less, so long as A and B have the same major
/// versions.
///
/// For example, v1.2 is compatible with v1.1, as it is likely only some additions of endpoints
/// on top of v1.1, but v1.1 would not be compatible with v1.2, as v1.1 cannot represent all of
/// v1.2, in a manner similar to set theory.
///
/// Warning: Matrix has a deprecation policy, and Matrix versioning is not as straight-forward
/// as this function makes it out to be. This function only exists to prune major version
/// differences, and versions too new for `self`.
///
/// This (considering if major versions are the same) is equivalent to a `self >= other` check.
pub fn is_superset_of(self, other: Self) -> bool {
self.repr() >= other.repr()
}
// Internal function to desugar the enum to a version repr
fn repr(&self) -> VersionRepr {
match self {
MatrixVersion::V1_0 => VersionRepr::new(1, 0),
MatrixVersion::V1_1 => VersionRepr::new(1, 1),
MatrixVersion::V1_2 => VersionRepr::new(1, 2),
}
}
}

View File

@ -1,8 +1,8 @@
//! [GET /_matrix/client/versions](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-versions) //! [GET /_matrix/client/versions](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-versions)
use std::collections::BTreeMap; use std::{collections::BTreeMap, convert::TryInto as _};
use ruma_api::ruma_api; use ruma_api::{ruma_api, MatrixVersion};
ruma_api! { ruma_api! {
metadata: { metadata: {
@ -41,4 +41,19 @@ impl Response {
pub fn new(versions: Vec<String>) -> Self { pub fn new(versions: Vec<String>) -> Self {
Self { versions, unstable_features: BTreeMap::new() } Self { versions, unstable_features: BTreeMap::new() }
} }
/// Extracts known Matrix versions from this response.
///
/// Matrix versions that Ruma cannot parse, or does not know about, are discarded.
pub fn known_versions(&self) -> Vec<MatrixVersion> {
let mut versions = vec![];
for s in &self.versions {
if let Ok(ver) = s.as_str().try_into() {
if !versions.contains(&ver) {
versions.push(ver)
}
}
}
versions
}
} }