diff --git a/Cargo.lock b/Cargo.lock index 7cf90e10a1..943a320d80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2047,6 +2047,7 @@ dependencies = [ "ruma-events", "serde", "serde_json", + "smallstr", ] [[package]] diff --git a/crates/ruma-client-api/src/discovery/get_supported_versions.rs b/crates/ruma-client-api/src/discovery/get_supported_versions.rs index 8fe8f050a9..0b0be02ada 100644 --- a/crates/ruma-client-api/src/discovery/get_supported_versions.rs +++ b/crates/ruma-client-api/src/discovery/get_supported_versions.rs @@ -10,6 +10,7 @@ use ruma_common::{ api::{request, response, Metadata, SupportedVersions}, metadata, }; +use smallstr::SmallString; const METADATA: Metadata = metadata! { method: GET, @@ -29,16 +30,22 @@ pub struct Request {} #[response(error = crate::Error)] pub struct Response { /// A list of Matrix client API protocol versions supported by the homeserver. - pub versions: Vec, + pub versions: Vec, /// Experimental features supported by the server. /// /// Servers can enable some unstable features only for some users, so this /// list might differ when an access token is provided. #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub unstable_features: BTreeMap, + pub unstable_features: BTreeMap, } +/// Opinionated optimized Version String type. +pub type Version = SmallString<[u8; 16]>; + +/// Opinionated optimized Feature String type. +pub type Feature = SmallString<[u8; 48]>; + impl Request { /// Creates an empty `Request`. pub fn new() -> Self { @@ -48,7 +55,7 @@ impl Request { impl Response { /// Creates a new `Response` with the given `versions`. - pub fn new(versions: Vec) -> Self { + pub fn new(versions: Vec) -> Self { Self { versions, unstable_features: BTreeMap::new() } } @@ -58,6 +65,9 @@ impl Response { /// Matrix versions that can't be parsed to a `MatrixVersion`, and features with the boolean /// value set to `false` are discarded. pub fn as_supported_versions(&self) -> SupportedVersions { - SupportedVersions::from_parts(&self.versions, &self.unstable_features) + SupportedVersions::from_parts( + self.versions.iter().map(Version::as_str), + self.unstable_features.iter().map(|(k, v)| (k.as_str(), v)), + ) } } diff --git a/crates/ruma-client-api/src/membership/joined_members.rs b/crates/ruma-client-api/src/membership/joined_members.rs index 13fcc1cfc7..878fbd4f23 100644 --- a/crates/ruma-client-api/src/membership/joined_members.rs +++ b/crates/ruma-client-api/src/membership/joined_members.rs @@ -61,14 +61,12 @@ pub mod v3 { #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)] pub struct RoomMember { /// The display name of the user. - #[serde(skip_serializing_if = "Option::is_none")] pub display_name: Option, /// The mxc avatar url of the user. /// /// If you activate the `compat-empty-string-null` feature, this field being an empty /// string in JSON will result in `None` here during deserialization. - #[serde(skip_serializing_if = "Option::is_none")] #[cfg_attr( feature = "compat-empty-string-null", serde(default, deserialize_with = "ruma_common::serde::empty_string_as_none") diff --git a/crates/ruma-client-api/src/sync/sync_events/v5.rs b/crates/ruma-client-api/src/sync/sync_events/v5.rs index 32271f2ba4..b9785da8f9 100644 --- a/crates/ruma-client-api/src/sync/sync_events/v5.rs +++ b/crates/ruma-client-api/src/sync/sync_events/v5.rs @@ -218,6 +218,7 @@ pub mod request { pub required_state: Vec<(StateEventType, StateKey)>, /// The maximum number of timeline events to return per room. + #[serde(default)] pub timeline_limit: UInt, } diff --git a/crates/ruma-common/src/api/metadata.rs b/crates/ruma-common/src/api/metadata.rs index 1e125da0df..eb600cf35d 100644 --- a/crates/ruma-common/src/api/metadata.rs +++ b/crates/ruma-common/src/api/metadata.rs @@ -1,6 +1,6 @@ use std::{ cmp::Ordering, - collections::{BTreeMap, BTreeSet}, + collections::BTreeSet, fmt::{Display, Write}, str::FromStr, }; @@ -1144,13 +1144,19 @@ impl SupportedVersions { /// /// Matrix versions that can't be parsed to a `MatrixVersion`, and features with the boolean /// value set to `false` are discarded. - pub fn from_parts(versions: &[String], unstable_features: &BTreeMap) -> Self { + pub fn from_parts<'a, Versions, Features>( + versions: Versions, + unstable_features: Features, + ) -> Self + where + Versions: Iterator, + Features: Iterator, + { Self { - versions: versions.iter().flat_map(|s| s.parse::()).collect(), + versions: versions.flat_map(|s| s.parse::()).collect(), features: unstable_features - .iter() .filter(|(_, enabled)| **enabled) - .map(|(feature, _)| feature.as_str().into()) + .map(|(feature, _)| feature.into()) .collect(), } } diff --git a/crates/ruma-common/src/push/condition.rs b/crates/ruma-common/src/push/condition.rs index 234daa5057..57e1a73def 100644 --- a/crates/ruma-common/src/push/condition.rs +++ b/crates/ruma-common/src/push/condition.rs @@ -503,13 +503,16 @@ impl StrExt for str { } fn matches_pattern(&self, pattern: &str, match_words: bool) -> bool { - let value = &self.to_lowercase(); - let pattern = &pattern.to_lowercase(); - if match_words { + if self.eq_ignore_ascii_case(pattern) { + return true; + } + + let value = &self.to_lowercase(); + let pattern = &pattern.to_lowercase(); value.matches_word(pattern) } else { - WildMatch::new(pattern).matches(value) + WildMatch::new_case_insensitive(pattern).matches(self) } } diff --git a/crates/ruma-events/src/room/canonical_alias.rs b/crates/ruma-events/src/room/canonical_alias.rs index cb4597c48b..37500251ff 100644 --- a/crates/ruma-events/src/room/canonical_alias.rs +++ b/crates/ruma-events/src/room/canonical_alias.rs @@ -36,6 +36,11 @@ impl RoomCanonicalAliasEventContent { pub fn new() -> Self { Self { alias: None, alt_aliases: Vec::new() } } + + /// Returns an iterator over the canonical alias and alt aliases + pub fn aliases(&self) -> impl Iterator { + self.alias.iter().chain(self.alt_aliases.iter()) + } } #[cfg(test)] diff --git a/crates/ruma-events/src/room/server_acl.rs b/crates/ruma-events/src/room/server_acl.rs index a1c49f8cfd..ffd58ffa68 100644 --- a/crates/ruma-events/src/room/server_acl.rs +++ b/crates/ruma-events/src/room/server_acl.rs @@ -102,11 +102,11 @@ impl RoomServerAclEventContent { } fn matches(a: &[String], s: &str) -> bool { - a.iter().map(String::as_str).any(|a| WildMatch::new(a).matches(s)) + a.iter().map(String::as_str).any(|a| WildMatch::new_case_insensitive(a).matches(s)) } fn contains(a: &[String], s: &str) -> bool { - a.iter().map(String::as_str).any(|a| a == s) + a.iter().map(String::as_str).any(|a| a.eq_ignore_ascii_case(s)) } } @@ -222,4 +222,19 @@ mod tests { assert!(!acl_event.is_allowed(server_name!("[2001:db8:1234::2]"))); assert!(acl_event.is_allowed(server_name!("[2001:db8:1234::1]"))); } + + #[test] + fn acl_case_insensitive() { + let acl_event = RoomServerAclEventContent { + allow_ip_literals: false, + allow: vec!["good.ServEr".to_owned()], + deny: vec!["bad.ServeR".to_owned()], + }; + assert!(!acl_event.is_allowed(server_name!("Bad.ServeR"))); + assert!(!acl_event.is_allowed(server_name!("bAD.sERvER"))); + assert!(!acl_event.is_allowed(server_name!("bAd.server"))); + assert!(acl_event.is_allowed(server_name!("good.ServEr"))); + assert!(acl_event.is_allowed(server_name!("good.server"))); + assert!(acl_event.is_allowed(server_name!("GOOD.SERVER"))); + } } diff --git a/crates/ruma-identity-service-api/Cargo.toml b/crates/ruma-identity-service-api/Cargo.toml index 0ccc20b375..499f23c5ca 100644 --- a/crates/ruma-identity-service-api/Cargo.toml +++ b/crates/ruma-identity-service-api/Cargo.toml @@ -22,6 +22,7 @@ js_int = { workspace = true, features = ["serde"] } ruma-common = { workspace = true, features = ["api"] } ruma-events = { workspace = true } serde = { workspace = true } +smallstr = { workspace = true } [dev-dependencies] serde_json = { workspace = true } diff --git a/crates/ruma-identity-service-api/src/discovery/get_supported_versions.rs b/crates/ruma-identity-service-api/src/discovery/get_supported_versions.rs index 7e4c5dcf00..437cf71c9d 100644 --- a/crates/ruma-identity-service-api/src/discovery/get_supported_versions.rs +++ b/crates/ruma-identity-service-api/src/discovery/get_supported_versions.rs @@ -16,6 +16,7 @@ use ruma_common::{ api::{request, response, Metadata, SupportedVersions}, metadata, }; +use smallstr::SmallString; const METADATA: Metadata = metadata! { method: GET, @@ -35,9 +36,15 @@ pub struct Request {} #[response] pub struct Response { /// A list of Matrix client API protocol versions supported by the endpoint. - pub versions: Vec, + pub versions: Vec, } +/// Opinionated optimized Version String type. +pub type Version = SmallString<[u8; 16]>; + +/// Opinionated optimized Feature String type. +pub type Feature = SmallString<[u8; 48]>; + impl Request { /// Creates an empty `Request`. pub fn new() -> Self { @@ -47,7 +54,7 @@ impl Request { impl Response { /// Creates a new `Response` with the given `versions`. - pub fn new(versions: Vec) -> Self { + pub fn new(versions: Vec) -> Self { Self { versions } } @@ -57,6 +64,9 @@ impl Response { /// Matrix versions that can't be parsed to a `MatrixVersion`, and features with the boolean /// value set to `false` are discarded. pub fn as_supported_versions(&self) -> SupportedVersions { - SupportedVersions::from_parts(&self.versions, &BTreeMap::new()) + SupportedVersions::from_parts( + self.versions.iter().map(Version::as_str), + BTreeMap::::new().iter().map(|(k, v)| (k.as_str(), v)), + ) } }