use std::{
cmp::Ordering,
collections::{BTreeMap, BTreeSet},
fmt::Display,
str::FromStr,
};
use bytes::BufMut;
use http::Method;
use ruma_macros::StringEnum;
use super::{auth_scheme::AuthScheme, error::UnknownVersionError, path_builder::PathBuilder};
use crate::{PrivOwnedStr, RoomVersionId, api::error::IntoHttpError, serde::slice_to_buf};
#[doc(hidden)]
#[macro_export]
macro_rules! metadata {
( @for $request_type:ty, $( $field:ident: $rhs:tt ),+ $(,)? ) => {
#[allow(deprecated)]
impl $crate::api::Metadata for $request_type {
$( $crate::metadata!(@field $field: $rhs); )+
}
};
( $( $field:ident: $rhs:tt ),+ $(,)? ) => {
$crate::metadata!{ @for Request, $( $field: $rhs),+ }
};
( @field method: $method:ident ) => {
const METHOD: $crate::exports::http::Method = $crate::exports::http::Method::$method;
};
( @field rate_limited: $rate_limited:literal ) => { const RATE_LIMITED: bool = $rate_limited; };
( @field authentication: $scheme:path ) => {
type Authentication = $scheme;
};
( @field path: $path:literal ) => {
type PathBuilder = $crate::api::path_builder::SinglePath;
const PATH_BUILDER: $crate::api::path_builder::SinglePath = $crate::api::path_builder::SinglePath::new($path);
};
( @field history: {
$( unstable $(($unstable_feature:literal))? => $unstable_path:literal, )*
$( stable ($stable_feature_only:literal) => $stable_feature_path:literal, )*
$( $version:literal $(| stable ($stable_feature:literal))? => $stable_rhs:tt, )*
} ) => {
$crate::metadata! {
@history_impl
$( unstable $( ($unstable_feature) )? => $unstable_path, )*
$( stable ($stable_feature_only) => $stable_feature_path, )*
$( $stable_rhs = $version $( | stable ($stable_feature) )?, )*
}
};
( @history_impl
$( unstable $(($unstable_feature:literal))? => $unstable_path:literal, )*
$( stable ($stable_feature_only:literal) => $stable_feature_path:literal, )*
$( $stable_path:literal = $version:literal $(| stable ($stable_feature:literal))?, )*
$( deprecated = $deprecated_version:literal, )?
$( removed = $removed_version:literal, )?
) => {
type PathBuilder = $crate::api::path_builder::VersionHistory;
const PATH_BUILDER: $crate::api::path_builder::VersionHistory = $crate::api::path_builder::VersionHistory::new(
&[ $(($crate::metadata!(@optional_feature $($unstable_feature)?), $unstable_path)),* ],
&[
$((
$crate::metadata!(@stable_path_selector stable($stable_feature_only)),
$stable_feature_path
),)*
$((
$crate::metadata!(@stable_path_selector $version $( | stable($stable_feature) )?),
$stable_path
),)*
],
$crate::metadata!(@optional_version $( $deprecated_version )?),
$crate::metadata!(@optional_version $( $removed_version )?),
);
};
( @optional_feature ) => { None };
( @optional_feature $feature:literal ) => { Some($feature) };
( @stable_path_selector stable($feature:literal)) => {
$crate::api::path_builder::StablePathSelector::Feature($feature)
};
( @stable_path_selector $version:literal | stable($feature:literal)) => {
$crate::api::path_builder::StablePathSelector::FeatureAndVersion {
feature: $feature,
version: $crate::api::MatrixVersion::from_lit(stringify!($version)),
}
};
( @stable_path_selector $version:literal) => {
$crate::api::path_builder::StablePathSelector::Version(
$crate::api::MatrixVersion::from_lit(stringify!($version))
)
};
( @optional_version ) => { None };
( @optional_version $version:literal ) => { Some($crate::api::MatrixVersion::from_lit(stringify!($version))) }
}
pub trait Metadata: Sized {
const METHOD: Method;
const RATE_LIMITED: bool;
type Authentication: AuthScheme;
type PathBuilder: PathBuilder;
const PATH_BUILDER: Self::PathBuilder;
fn empty_request_body<B>() -> B
where
B: Default + BufMut,
{
if Self::METHOD == Method::GET { Default::default() } else { slice_to_buf(b"{}") }
}
fn make_endpoint_url(
path_builder_input: <Self::PathBuilder as PathBuilder>::Input<'_>,
base_url: &str,
path_args: &[&dyn Display],
query_string: &str,
) -> Result<String, IntoHttpError> {
Self::PATH_BUILDER.make_endpoint_url(path_builder_input, base_url, path_args, query_string)
}
#[doc(hidden)]
fn _path_parameters() -> Vec<&'static str> {
Self::PATH_BUILDER._path_parameters()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
pub enum MatrixVersion {
V1_0,
V1_1,
V1_2,
V1_3,
V1_4,
V1_5,
V1_6,
V1_7,
V1_8,
V1_9,
V1_10,
V1_11,
V1_12,
V1_13,
V1_14,
V1_15,
V1_16,
V1_17,
}
impl TryFrom<&str> for MatrixVersion {
type Error = UnknownVersionError;
fn try_from(value: &str) -> Result<MatrixVersion, Self::Error> {
use MatrixVersion::*;
Ok(match value {
"r0.2.0" | "r0.2.1" | "r0.3.0" |
"r0.5.0" | "r0.6.0" | "r0.6.1" => V1_0,
"v1.1" => V1_1,
"v1.2" => V1_2,
"v1.3" => V1_3,
"v1.4" => V1_4,
"v1.5" => V1_5,
"v1.6" => V1_6,
"v1.7" => V1_7,
"v1.8" => V1_8,
"v1.9" => V1_9,
"v1.10" => V1_10,
"v1.11" => V1_11,
"v1.12" => V1_12,
"v1.13" => V1_13,
"v1.14" => V1_14,
"v1.15" => V1_15,
"v1.16" => V1_16,
"v1.17" => V1_17,
_ => return Err(UnknownVersionError),
})
}
}
impl FromStr for MatrixVersion {
type Err = UnknownVersionError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::try_from(s)
}
}
impl MatrixVersion {
pub fn is_superset_of(self, other: Self) -> bool {
self >= other
}
pub const fn as_str(self) -> Option<&'static str> {
let string = match self {
MatrixVersion::V1_0 => return None,
MatrixVersion::V1_1 => "v1.1",
MatrixVersion::V1_2 => "v1.2",
MatrixVersion::V1_3 => "v1.3",
MatrixVersion::V1_4 => "v1.4",
MatrixVersion::V1_5 => "v1.5",
MatrixVersion::V1_6 => "v1.6",
MatrixVersion::V1_7 => "v1.7",
MatrixVersion::V1_8 => "v1.8",
MatrixVersion::V1_9 => "v1.9",
MatrixVersion::V1_10 => "v1.10",
MatrixVersion::V1_11 => "v1.11",
MatrixVersion::V1_12 => "v1.12",
MatrixVersion::V1_13 => "v1.13",
MatrixVersion::V1_14 => "v1.14",
MatrixVersion::V1_15 => "v1.15",
MatrixVersion::V1_16 => "v1.16",
MatrixVersion::V1_17 => "v1.17",
};
Some(string)
}
const fn into_parts(self) -> (u8, u8) {
match self {
MatrixVersion::V1_0 => (1, 0),
MatrixVersion::V1_1 => (1, 1),
MatrixVersion::V1_2 => (1, 2),
MatrixVersion::V1_3 => (1, 3),
MatrixVersion::V1_4 => (1, 4),
MatrixVersion::V1_5 => (1, 5),
MatrixVersion::V1_6 => (1, 6),
MatrixVersion::V1_7 => (1, 7),
MatrixVersion::V1_8 => (1, 8),
MatrixVersion::V1_9 => (1, 9),
MatrixVersion::V1_10 => (1, 10),
MatrixVersion::V1_11 => (1, 11),
MatrixVersion::V1_12 => (1, 12),
MatrixVersion::V1_13 => (1, 13),
MatrixVersion::V1_14 => (1, 14),
MatrixVersion::V1_15 => (1, 15),
MatrixVersion::V1_16 => (1, 16),
MatrixVersion::V1_17 => (1, 17),
}
}
const fn from_parts(major: u8, minor: u8) -> Result<Self, UnknownVersionError> {
match (major, minor) {
(1, 0) => Ok(MatrixVersion::V1_0),
(1, 1) => Ok(MatrixVersion::V1_1),
(1, 2) => Ok(MatrixVersion::V1_2),
(1, 3) => Ok(MatrixVersion::V1_3),
(1, 4) => Ok(MatrixVersion::V1_4),
(1, 5) => Ok(MatrixVersion::V1_5),
(1, 6) => Ok(MatrixVersion::V1_6),
(1, 7) => Ok(MatrixVersion::V1_7),
(1, 8) => Ok(MatrixVersion::V1_8),
(1, 9) => Ok(MatrixVersion::V1_9),
(1, 10) => Ok(MatrixVersion::V1_10),
(1, 11) => Ok(MatrixVersion::V1_11),
(1, 12) => Ok(MatrixVersion::V1_12),
(1, 13) => Ok(MatrixVersion::V1_13),
(1, 14) => Ok(MatrixVersion::V1_14),
(1, 15) => Ok(MatrixVersion::V1_15),
(1, 16) => Ok(MatrixVersion::V1_16),
(1, 17) => Ok(MatrixVersion::V1_17),
_ => Err(UnknownVersionError),
}
}
#[doc(hidden)]
pub const fn from_lit(lit: &'static str) -> Self {
use konst::{option, primitive::parse_u8, result, string};
let major: u8;
let minor: u8;
let mut lit_iter = string::split(lit, ".").next();
{
let (checked_first, checked_split) = option::unwrap!(lit_iter);
major = result::unwrap_or_else!(parse_u8(checked_first), |_| panic!(
"major version is not a valid number"
));
lit_iter = checked_split.next();
}
match lit_iter {
Some((checked_second, checked_split)) => {
minor = result::unwrap_or_else!(parse_u8(checked_second), |_| panic!(
"minor version is not a valid number"
));
lit_iter = checked_split.next();
}
None => panic!("could not find dot to denote second number"),
}
if lit_iter.is_some() {
panic!("version literal contains more than one dot")
}
result::unwrap_or_else!(Self::from_parts(major, minor), |_| panic!(
"not a valid version literal"
))
}
pub(super) const fn const_ord(&self, other: &Self) -> Ordering {
let self_parts = self.into_parts();
let other_parts = other.into_parts();
use konst::primitive::cmp::cmp_u8;
let major_ord = cmp_u8(self_parts.0, other_parts.0);
if major_ord.is_ne() { major_ord } else { cmp_u8(self_parts.1, other_parts.1) }
}
pub(super) const fn is_legacy(&self) -> bool {
let self_parts = self.into_parts();
use konst::primitive::cmp::cmp_u8;
cmp_u8(self_parts.0, 1).is_eq() && cmp_u8(self_parts.1, 0).is_eq()
}
pub fn default_room_version(&self) -> RoomVersionId {
match self {
MatrixVersion::V1_0
| MatrixVersion::V1_1
| MatrixVersion::V1_2 => RoomVersionId::V6,
MatrixVersion::V1_3
| MatrixVersion::V1_4
| MatrixVersion::V1_5 => RoomVersionId::V9,
MatrixVersion::V1_6
| MatrixVersion::V1_7
| MatrixVersion::V1_8
| MatrixVersion::V1_9
| MatrixVersion::V1_10
| MatrixVersion::V1_11
| MatrixVersion::V1_12
| MatrixVersion::V1_13 => RoomVersionId::V10,
| MatrixVersion::V1_14
| MatrixVersion::V1_15 => RoomVersionId::V11,
MatrixVersion::V1_16
| MatrixVersion::V1_17 => RoomVersionId::V12,
}
}
}
#[derive(Debug, Clone)]
#[allow(clippy::exhaustive_structs)]
pub struct SupportedVersions {
pub versions: BTreeSet<MatrixVersion>,
pub features: BTreeSet<FeatureFlag>,
}
impl SupportedVersions {
pub fn from_parts(versions: &[String], unstable_features: &BTreeMap<String, bool>) -> Self {
Self {
versions: versions.iter().flat_map(|s| s.parse::<MatrixVersion>()).collect(),
features: unstable_features
.iter()
.filter(|(_, enabled)| **enabled)
.map(|(feature, _)| feature.as_str().into())
.collect(),
}
}
}
#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
#[derive(Clone, StringEnum, Hash)]
#[non_exhaustive]
pub enum FeatureFlag {
#[ruma_enum(rename = "fi.mau.msc2246")]
Msc2246,
#[ruma_enum(rename = "org.matrix.msc2432")]
Msc2432,
#[ruma_enum(rename = "fi.mau.msc2659")]
Msc2659,
#[ruma_enum(rename = "fi.mau.msc2659.stable")]
Msc2659Stable,
#[cfg(feature = "unstable-msc2666")]
#[ruma_enum(rename = "uk.half-shot.msc2666.query_mutual_rooms")]
Msc2666,
#[ruma_enum(rename = "org.matrix.msc3030")]
Msc3030,
#[ruma_enum(rename = "org.matrix.msc3882")]
Msc3882,
#[ruma_enum(rename = "org.matrix.msc3916")]
Msc3916,
#[ruma_enum(rename = "org.matrix.msc3916.stable")]
Msc3916Stable,
#[cfg(feature = "unstable-msc4108")]
#[ruma_enum(rename = "org.matrix.msc4108")]
Msc4108,
#[cfg(feature = "unstable-msc4140")]
#[ruma_enum(rename = "org.matrix.msc4140")]
Msc4140,
#[cfg(feature = "unstable-msc4186")]
#[ruma_enum(rename = "org.matrix.simplified_msc3575")]
Msc4186,
#[cfg(feature = "unstable-msc4380")]
#[ruma_enum(rename = "org.matrix.msc4380")]
Msc4380,
#[doc(hidden)]
_Custom(PrivOwnedStr),
}