api: Add VersionHistory
const fn new
This commit is contained in:
parent
7e1fd603e6
commit
e783d77db7
@ -57,6 +57,7 @@ indexmap = { version = "1.9.1", features = ["serde"] }
|
||||
itoa = "1.0.1"
|
||||
js_int = { version = "0.2.0", features = ["serde"] }
|
||||
js_option = "0.1.0"
|
||||
konst = { version = "0.2.19", features = ["rust_1_64", "alloc"] }
|
||||
percent-encoding = "2.1.0"
|
||||
phf = { version = "0.10.1", features = ["macros"], optional = true }
|
||||
pulldown-cmark = { version = "0.9.1", default-features = false, optional = true }
|
||||
@ -83,7 +84,7 @@ assert_matches = "1.5.0"
|
||||
assign = "1.1.1"
|
||||
http = "0.2.2"
|
||||
maplit = "1.0.2"
|
||||
trybuild = "1.0.42"
|
||||
trybuild = "1.0.71"
|
||||
|
||||
[[bench]]
|
||||
name = "event_deserialize"
|
||||
|
@ -1,4 +1,5 @@
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
fmt::{self, Display, Write},
|
||||
str::FromStr,
|
||||
};
|
||||
@ -158,6 +159,144 @@ pub struct VersionHistory {
|
||||
}
|
||||
|
||||
impl VersionHistory {
|
||||
/// Constructs an instance of [`VersionHistory`], erroring on compilation if it does not pass
|
||||
/// invariants.
|
||||
///
|
||||
/// Specifically, this checks the following invariants:
|
||||
/// - Path Arguments are equal (in order, amount, and argument name) in all path strings
|
||||
/// - In stable_paths:
|
||||
/// - matrix versions are in ascending order
|
||||
/// - no matrix version is referenced twice
|
||||
/// - deprecated's version comes after the latest version mentioned in stable_paths, and only if
|
||||
/// any stable path is defined
|
||||
/// - removed comes after deprecated, or after the latest referenced stable_paths, like
|
||||
/// deprecated
|
||||
pub const fn new(
|
||||
unstable_paths: &'static [&'static str],
|
||||
stable_paths: &'static [(MatrixVersion, &'static str)],
|
||||
deprecated: Option<MatrixVersion>,
|
||||
removed: Option<MatrixVersion>,
|
||||
) -> Self {
|
||||
use konst::{iter, slice, string};
|
||||
|
||||
const fn check_path_is_valid(path: &'static str) {
|
||||
iter::for_each!(path_b in slice::iter(path.as_bytes()) => {
|
||||
match *path_b {
|
||||
0x21..=0x7E => {},
|
||||
_ => panic!("path contains invalid (non-ascii or whitespace) characters")
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const fn check_path_args_equal(first: &'static str, second: &'static str) {
|
||||
let mut second_iter = string::split(second, "/").next();
|
||||
|
||||
iter::for_each!(first_s in string::split(first, "/") => {
|
||||
if let Some(first_arg) = string::strip_prefix(first_s, ":") {
|
||||
let second_next_arg: Option<&'static str> = loop {
|
||||
let (second_s, second_n_iter) = match second_iter {
|
||||
Some(tuple) => tuple,
|
||||
None => break None,
|
||||
};
|
||||
|
||||
let maybe_second_arg = string::strip_prefix(second_s, ":");
|
||||
|
||||
second_iter = second_n_iter.next();
|
||||
|
||||
if let Some(second_arg) = maybe_second_arg {
|
||||
break Some(second_arg);
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(second_next_arg) = second_next_arg {
|
||||
if !string::eq_str(second_next_arg, first_arg) {
|
||||
panic!("Path Arguments do not match");
|
||||
}
|
||||
} else {
|
||||
panic!("Amount of Path Arguments do not match");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// If second iterator still has some values, empty first.
|
||||
while let Some((second_s, second_n_iter)) = second_iter {
|
||||
if string::starts_with(second_s, ":") {
|
||||
panic!("Amount of Path Arguments do not match");
|
||||
}
|
||||
second_iter = second_n_iter.next();
|
||||
}
|
||||
}
|
||||
|
||||
// The path we're going to use to compare all other paths with
|
||||
let ref_path: &str = if let Some(s) = unstable_paths.first() {
|
||||
s
|
||||
} else if let Some((_, s)) = stable_paths.first() {
|
||||
s
|
||||
} else {
|
||||
panic!("No paths supplied")
|
||||
};
|
||||
|
||||
iter::for_each!(unstable_path in slice::iter(unstable_paths) => {
|
||||
check_path_is_valid(unstable_path);
|
||||
check_path_args_equal(ref_path, unstable_path);
|
||||
});
|
||||
|
||||
let mut prev_seen_version: Option<MatrixVersion> = None;
|
||||
|
||||
iter::for_each!(stable_path in slice::iter(stable_paths) => {
|
||||
check_path_is_valid(stable_path.1);
|
||||
check_path_args_equal(ref_path, stable_path.1);
|
||||
|
||||
let current_version = stable_path.0;
|
||||
|
||||
if let Some(prev_seen_version) = prev_seen_version {
|
||||
let cmp_result = current_version.const_ord(&prev_seen_version);
|
||||
|
||||
if cmp_result.is_eq() {
|
||||
// Found a duplicate, current == previous
|
||||
panic!("Duplicate matrix version in stable_paths")
|
||||
} else if cmp_result.is_lt() {
|
||||
// Found an older version, current < previous
|
||||
panic!("No ascending order in stable_paths")
|
||||
}
|
||||
}
|
||||
|
||||
prev_seen_version = Some(current_version);
|
||||
});
|
||||
|
||||
if let Some(deprecated) = deprecated {
|
||||
if let Some(prev_seen_version) = prev_seen_version {
|
||||
let ord_result = prev_seen_version.const_ord(&deprecated);
|
||||
if ord_result.is_eq() {
|
||||
// prev_seen_version == deprecated
|
||||
panic!("deprecated version is equal to latest stable path version")
|
||||
} else if ord_result.is_gt() {
|
||||
// prev_seen_version > deprecated
|
||||
panic!("deprecated version is older than latest stable path version")
|
||||
}
|
||||
} else {
|
||||
panic!("Defined deprecated version while no stable path exists")
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(removed) = removed {
|
||||
if let Some(deprecated) = deprecated {
|
||||
let ord_result = deprecated.const_ord(&removed);
|
||||
if ord_result.is_eq() {
|
||||
// deprecated == removed
|
||||
panic!("removed version is equal to deprecated version")
|
||||
} else if ord_result.is_gt() {
|
||||
// deprecated > removed
|
||||
panic!("removed version is older than deprecated version")
|
||||
}
|
||||
} else {
|
||||
panic!("Defined removed version while no deprecated version exists")
|
||||
}
|
||||
}
|
||||
|
||||
VersionHistory { unstable_paths, stable_paths, deprecated, removed }
|
||||
}
|
||||
|
||||
// This function helps picks the right path (or an error) from a set of Matrix versions.
|
||||
fn select_path(
|
||||
&self,
|
||||
@ -409,7 +548,7 @@ impl MatrixVersion {
|
||||
}
|
||||
|
||||
/// Decompose the Matrix version into its major and minor number.
|
||||
pub fn into_parts(self) -> (u8, u8) {
|
||||
pub const fn into_parts(self) -> (u8, u8) {
|
||||
match self {
|
||||
MatrixVersion::V1_0 => (1, 0),
|
||||
MatrixVersion::V1_1 => (1, 1),
|
||||
@ -431,6 +570,21 @@ impl MatrixVersion {
|
||||
}
|
||||
}
|
||||
|
||||
// Internal function to do ordering in const-fn contexts
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the default [`RoomVersionId`] for this `MatrixVersion`.
|
||||
pub fn default_room_version(&self) -> RoomVersionId {
|
||||
match self {
|
||||
|
@ -30,15 +30,15 @@ const METADATA: Metadata = Metadata {
|
||||
rate_limited: false,
|
||||
authentication: AuthScheme::None,
|
||||
|
||||
history: VersionHistory {
|
||||
unstable_paths: &["/_matrix/client/unstable/directory/room/:room_alias"],
|
||||
stable_paths: &[
|
||||
history: VersionHistory::new(
|
||||
&["/_matrix/client/unstable/directory/room/:room_alias"],
|
||||
&[
|
||||
(MatrixVersion::V1_0, "/_matrix/client/r0/directory/room/:room_alias"),
|
||||
(MatrixVersion::V1_1, "/_matrix/client/v3/directory/room/:room_alias"),
|
||||
],
|
||||
deprecated: Some(MatrixVersion::V1_2),
|
||||
removed: Some(MatrixVersion::V1_3),
|
||||
},
|
||||
Some(MatrixVersion::V1_2),
|
||||
Some(MatrixVersion::V1_3),
|
||||
),
|
||||
};
|
||||
|
||||
impl OutgoingRequest for Request {
|
||||
|
Loading…
x
Reference in New Issue
Block a user