api: Add Metadata::versioning_decision_for
This commit is contained in:
parent
873270db1d
commit
f0177dc429
@ -15,6 +15,7 @@
|
||||
use std::{convert::TryInto as _, error::Error as StdError, fmt};
|
||||
|
||||
use bytes::BufMut;
|
||||
use tracing::warn;
|
||||
|
||||
use crate::UserId;
|
||||
|
||||
@ -195,7 +196,7 @@ pub use ruma_macros::ruma_api;
|
||||
pub mod error;
|
||||
mod metadata;
|
||||
|
||||
pub use metadata::{MatrixVersion, Metadata};
|
||||
pub use metadata::{MatrixVersion, Metadata, VersioningDecision};
|
||||
|
||||
use error::{FromHttpRequestError, FromHttpResponseError, IntoHttpError};
|
||||
|
||||
@ -401,10 +402,6 @@ pub enum AuthScheme {
|
||||
// This function helps picks the right path (or an error) from a set of matrix versions.
|
||||
//
|
||||
// This function needs to be public, yet hidden, as all `try_into_http_request`s would be using it.
|
||||
//
|
||||
// Note this assumes that `versions`;
|
||||
// - at least has 1 element
|
||||
// - have all elements sorted by its `.into_parts` representation.
|
||||
#[doc(hidden)]
|
||||
pub fn select_path<'a>(
|
||||
versions: &'_ [MatrixVersion],
|
||||
@ -413,50 +410,48 @@ pub fn select_path<'a>(
|
||||
r0: Option<fmt::Arguments<'a>>,
|
||||
stable: Option<fmt::Arguments<'a>>,
|
||||
) -> Result<fmt::Arguments<'a>, IntoHttpError> {
|
||||
let greater_or_equal_any =
|
||||
|version: MatrixVersion| versions.iter().any(|v| v.is_superset_of(version));
|
||||
let greater_or_equal_all =
|
||||
|version: MatrixVersion| versions.iter().all(|v| v.is_superset_of(version));
|
||||
|
||||
let is_stable_any = metadata.added.map(greater_or_equal_any).unwrap_or(false);
|
||||
|
||||
let is_removed_all = metadata.removed.map(greater_or_equal_all).unwrap_or(false);
|
||||
|
||||
// Only when all the versions (e.g. 1.6-9) are compatible with the version that added it (e.g.
|
||||
// 1.1), yet are also "after" the version that removed it (e.g. 1.3), then we return that error.
|
||||
// Otherwise, this all versions may fall into a different major range, such as 2.X, where
|
||||
// "after" and "compatible" do not exist with the 1.X range, so we at least need to make sure
|
||||
// that the versions are part of the same "range" through the `added` check.
|
||||
if is_stable_any && is_removed_all {
|
||||
return Err(IntoHttpError::EndpointRemoved(metadata.removed.unwrap()));
|
||||
}
|
||||
|
||||
if is_stable_any {
|
||||
let is_deprecated_any = metadata.deprecated.map(greater_or_equal_any).unwrap_or(false);
|
||||
|
||||
if is_deprecated_any {
|
||||
let is_removed_any = metadata.removed.map(greater_or_equal_any).unwrap_or(false);
|
||||
|
||||
if is_removed_any {
|
||||
tracing::warn!("endpoint {} is deprecated, but also removed in one of more server-passed versions: {:?}", metadata.name, versions)
|
||||
} else {
|
||||
tracing::warn!(
|
||||
"endpoint {} is deprecated in one of more server-passed versions: {:?}",
|
||||
metadata.name,
|
||||
versions
|
||||
)
|
||||
match metadata.versioning_decision_for(versions) {
|
||||
VersioningDecision::Removed => Err(IntoHttpError::EndpointRemoved(
|
||||
metadata.removed.expect("VersioningDecision::Removed implies metadata.removed"),
|
||||
)),
|
||||
VersioningDecision::Stable { any_deprecated, all_deprecated, any_removed } => {
|
||||
if any_removed {
|
||||
if all_deprecated {
|
||||
warn!(
|
||||
"endpoint {} is removed in some (and deprecated in ALL) of the following versions: {:?}",
|
||||
metadata.name,
|
||||
versions
|
||||
);
|
||||
} else if any_deprecated {
|
||||
warn!(
|
||||
"endpoint {} is removed (and deprecated) in some of the following versions: {:?}",
|
||||
metadata.name,
|
||||
versions
|
||||
);
|
||||
} else {
|
||||
unreachable!("any_removed implies *_deprecated");
|
||||
}
|
||||
} else if all_deprecated {
|
||||
warn!(
|
||||
"endpoint {} is deprecated in ALL of the following versions: {:?}",
|
||||
metadata.name, versions
|
||||
);
|
||||
} else if any_deprecated {
|
||||
warn!(
|
||||
"endpoint {} is deprecated in some of the following versions: {:?}",
|
||||
metadata.name, versions
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(r0) = r0 {
|
||||
if versions.iter().all(|&v| v == MatrixVersion::V1_0) {
|
||||
// Endpoint was added in 1.0, we return the r0 variant.
|
||||
return Ok(r0);
|
||||
if let Some(r0) = r0 {
|
||||
if versions.iter().all(|&v| v == MatrixVersion::V1_0) {
|
||||
// Endpoint was added in 1.0, we return the r0 variant.
|
||||
return Ok(r0);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(stable.expect("metadata.added enforces the stable path to exist"))
|
||||
}
|
||||
|
||||
return Ok(stable.expect("metadata.added enforces the stable path to exist"));
|
||||
VersioningDecision::Unstable => unstable.ok_or(IntoHttpError::NoUnstablePath),
|
||||
}
|
||||
|
||||
unstable.ok_or(IntoHttpError::NoUnstablePath)
|
||||
}
|
||||
|
@ -59,6 +59,69 @@ pub struct Metadata {
|
||||
pub removed: Option<MatrixVersion>,
|
||||
}
|
||||
|
||||
impl Metadata {
|
||||
/// Will decide how a particular set of matrix versions sees an endpoint.
|
||||
///
|
||||
/// It will pick `Stable` over `R0` and `Unstable`. It'll return `Deprecated` or `Removed` only
|
||||
/// if all versions denote it.
|
||||
///
|
||||
/// In other words, if in any version it tells it supports the endpoint in a stable fashion,
|
||||
/// this will return `Stable`, even if some versions in this set will denote deprecation or
|
||||
/// removal.
|
||||
///
|
||||
/// If resulting [`VersioningDecision`] is `Stable`, it will also detail if any version denoted
|
||||
/// deprecation or removal.
|
||||
pub fn versioning_decision_for(&self, versions: &[MatrixVersion]) -> VersioningDecision {
|
||||
let greater_or_equal_any =
|
||||
|version: MatrixVersion| versions.iter().any(|v| v.is_superset_of(version));
|
||||
let greater_or_equal_all =
|
||||
|version: MatrixVersion| versions.iter().all(|v| v.is_superset_of(version));
|
||||
|
||||
// Check if all versions removed this endpoint.
|
||||
if self.removed.map(greater_or_equal_all).unwrap_or(false) {
|
||||
return VersioningDecision::Removed;
|
||||
}
|
||||
|
||||
// Check if *any* version marks this endpoint as stable.
|
||||
if self.added.map(greater_or_equal_any).unwrap_or(false) {
|
||||
let all_deprecated = self.deprecated.map(greater_or_equal_all).unwrap_or(false);
|
||||
|
||||
return VersioningDecision::Stable {
|
||||
any_deprecated: all_deprecated
|
||||
|| self.deprecated.map(greater_or_equal_any).unwrap_or(false),
|
||||
all_deprecated,
|
||||
any_removed: self.removed.map(greater_or_equal_any).unwrap_or(false),
|
||||
};
|
||||
}
|
||||
|
||||
VersioningDecision::Unstable
|
||||
}
|
||||
}
|
||||
|
||||
/// A versioning "decision" derived from a set of matrix versions.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[allow(clippy::exhaustive_enums)]
|
||||
pub enum VersioningDecision {
|
||||
/// The unstable endpoint should be used.
|
||||
Unstable,
|
||||
/// The stable endpoint should be used.
|
||||
///
|
||||
/// Note, in the special case that all versions note [v1.0](MatrixVersion::V1_0), and the
|
||||
/// [`r0_path`](Metadata::r0_path) is not `None`, that path should be used.
|
||||
Stable {
|
||||
/// If any version denoted deprecation.
|
||||
any_deprecated: bool,
|
||||
|
||||
/// If *all* versions denoted deprecation.
|
||||
all_deprecated: bool,
|
||||
|
||||
/// If any version denoted removal.
|
||||
any_removed: bool,
|
||||
},
|
||||
/// This endpoint was removed in all versions, it should not be used.
|
||||
Removed,
|
||||
}
|
||||
|
||||
/// The Matrix versions Ruma currently understands to exist.
|
||||
///
|
||||
/// Matrix, since fall 2021, has a quarterly release schedule, using a global `vX.Y` versioning
|
||||
|
Loading…
x
Reference in New Issue
Block a user