diff --git a/crates/ruma-common/CHANGELOG.md b/crates/ruma-common/CHANGELOG.md index 5b016c59..e7c4b76a 100644 --- a/crates/ruma-common/CHANGELOG.md +++ b/crates/ruma-common/CHANGELOG.md @@ -49,6 +49,7 @@ Improvements: * Implement `From` for `identifiers::matrix_uri::MatrixId` * Add unstable default push rule to ignore room server ACLs events (MSC3786) * Add unstable support for private read receipts (MSC2285) +* Add unstable support for filtering public rooms by room type (MSC3827) # 0.9.2 diff --git a/crates/ruma-common/Cargo.toml b/crates/ruma-common/Cargo.toml index ee74e442..69b7ea78 100644 --- a/crates/ruma-common/Cargo.toml +++ b/crates/ruma-common/Cargo.toml @@ -51,6 +51,7 @@ unstable-msc3552 = ["unstable-msc3551"] unstable-msc3553 = ["unstable-msc3552"] unstable-msc3554 = ["unstable-msc1767"] unstable-msc3786 = [] +unstable-msc3827 = [] [dependencies] base64 = "0.13.0" diff --git a/crates/ruma-common/src/directory.rs b/crates/ruma-common/src/directory.rs index 5f469dd1..f9e34f98 100644 --- a/crates/ruma-common/src/directory.rs +++ b/crates/ruma-common/src/directory.rs @@ -1,15 +1,18 @@ //! Common types for room directory endpoints. -use crate::{ - serde::{Incoming, StringEnum}, - OwnedRoomAliasId, OwnedRoomId, -}; use js_int::UInt; use serde::{Deserialize, Serialize}; +#[cfg(feature = "unstable-msc3827")] +mod filter_room_type_serde; mod room_network_serde; -use crate::{OwnedMxcUri, PrivOwnedStr}; +#[cfg(feature = "unstable-msc3827")] +use crate::room::RoomType; +use crate::{ + serde::{Incoming, StringEnum}, + OwnedMxcUri, OwnedRoomAliasId, OwnedRoomId, PrivOwnedStr, +}; /// A chunk of a room list response, describing one room. /// @@ -62,6 +65,19 @@ pub struct PublicRoomsChunk { /// The join rule of the room. #[serde(default, skip_serializing_if = "crate::serde::is_default")] pub join_rule: PublicRoomJoinRule, + + /// The type of room from `m.room.create`, if any. + /// + /// This field uses the unstable prefix from [MSC3827]. + /// + /// [MSC3827]: https://github.com/matrix-org/matrix-spec-proposals/pull/3827 + #[cfg(feature = "unstable-msc3827")] + #[serde( + rename = "org.matrix.msc3827.room_type", + alias = "room_type", + skip_serializing_if = "Option::is_none" + )] + pub room_type: Option, } /// Initial set of mandatory fields of `PublicRoomsChunk`. @@ -101,6 +117,8 @@ impl From for PublicRoomsChunk { guest_can_join, avatar_url: None, join_rule: PublicRoomJoinRule::default(), + #[cfg(feature = "unstable-msc3827")] + room_type: None, } } } @@ -113,6 +131,22 @@ pub struct Filter<'a> { /// A string to search for in the room metadata, e.g. name, topic, canonical alias etc. #[serde(skip_serializing_if = "Option::is_none")] pub generic_search_term: Option<&'a str>, + + /// The room types to include in the results. + /// + /// Includes all room types if it is empty. + /// + /// This field uses the unstable prefix from [MSC3827]. + /// + /// [MSC3827]: https://github.com/matrix-org/matrix-spec-proposals/pull/3827 + #[cfg(feature = "unstable-msc3827")] + #[serde( + rename = "org.matrix.msc3827.room_types", + alias = "room_types", + default, + skip_serializing_if = "Vec::is_empty" + )] + pub room_types: Vec, } impl Filter<'_> { @@ -171,11 +205,68 @@ impl Default for PublicRoomJoinRule { } } +/// An enum of possible room types to filter. +/// +/// This type can hold an arbitrary string. To build this with a custom value, convert it from an +/// `Option` with `::from()` / `.into()`. [`RoomTypeFilter::Default`] can be constructed +/// from `None`. +/// +/// To check for values that are not available as a documented variant here, use its string +/// representation, obtained through [`.as_str()`](Self::as_str()). +#[cfg(feature = "unstable-msc3827")] +#[derive(Clone, Debug, PartialEq, Eq)] +#[non_exhaustive] +pub enum RoomTypeFilter { + /// The default room type, defined without a `room_type`. + Default, + + /// A space. + Space, + + /// A custom room type. + #[doc(hidden)] + _Custom(PrivOwnedStr), +} + +#[cfg(feature = "unstable-msc3827")] +impl RoomTypeFilter { + /// Get the string representation of this `RoomTypeFilter`. + /// + /// [`RoomTypeFilter::Default`] returns `None`. + pub fn as_str(&self) -> Option<&str> { + match self { + RoomTypeFilter::Default => None, + RoomTypeFilter::Space => Some("m.space"), + RoomTypeFilter::_Custom(s) => Some(&s.0), + } + } +} + +#[cfg(feature = "unstable-msc3827")] +impl From> for RoomTypeFilter +where + T: AsRef + Into>, +{ + fn from(s: Option) -> Self { + match s { + None => Self::Default, + Some(s) => match s.as_ref() { + "m.space" => Self::Space, + _ => Self::_Custom(PrivOwnedStr(s.into())), + }, + } + } +} + #[cfg(test)] mod tests { + #[cfg(feature = "unstable-msc3827")] + use assert_matches::assert_matches; use serde_json::{from_value as from_json_value, json, to_value as to_json_value}; - use super::{IncomingRoomNetwork, RoomNetwork}; + #[cfg(feature = "unstable-msc3827")] + use super::RoomTypeFilter; + use super::{Filter, IncomingFilter, IncomingRoomNetwork, RoomNetwork}; #[test] fn serialize_matrix_network_only() { @@ -242,4 +333,59 @@ mod tests { "`include_all_networks = true` and `third_party_instance_id` are mutually exclusive." ); } + + #[test] + fn serialize_filter_empty() { + let filter = Filter::default(); + let json = json!({}); + assert_eq!(to_json_value(filter).unwrap(), json); + } + + #[test] + fn deserialize_filter_empty() { + let json = json!({}); + let filter = from_json_value::(json).unwrap(); + assert_eq!(filter.generic_search_term, None); + #[cfg(feature = "unstable-msc3827")] + assert_eq!(filter.room_types.len(), 0); + } + + #[cfg(feature = "unstable-msc3827")] + #[test] + fn serialize_filter_room_types() { + let filter = Filter { + generic_search_term: None, + room_types: vec![ + RoomTypeFilter::Default, + RoomTypeFilter::Space, + Some("custom_type").into(), + ], + }; + let json = json!({ "org.matrix.msc3827.room_types": [null, "m.space", "custom_type"] }); + assert_eq!(to_json_value(filter).unwrap(), json); + } + + #[cfg(feature = "unstable-msc3827")] + #[test] + fn deserialize_filter_room_types_unstable() { + let json = json!({ "org.matrix.msc3827.room_types": [null, "m.space", "custom_type"] }); + let filter = from_json_value::(json).unwrap(); + assert_eq!(filter.room_types.len(), 3); + assert_eq!(filter.room_types[0], RoomTypeFilter::Default); + assert_eq!(filter.room_types[1], RoomTypeFilter::Space); + assert_matches!(filter.room_types[2], RoomTypeFilter::_Custom(_)); + assert_eq!(filter.room_types[2].as_str(), Some("custom_type")); + } + + #[cfg(feature = "unstable-msc3827")] + #[test] + fn deserialize_filter_room_types_stable() { + let json = json!({ "room_types": [null, "m.space", "custom_type"] }); + let filter = from_json_value::(json).unwrap(); + assert_eq!(filter.room_types.len(), 3); + assert_eq!(filter.room_types[0], RoomTypeFilter::Default); + assert_eq!(filter.room_types[1], RoomTypeFilter::Space); + assert_matches!(filter.room_types[2], RoomTypeFilter::_Custom(_)); + assert_eq!(filter.room_types[2].as_str(), Some("custom_type")); + } } diff --git a/crates/ruma-common/src/directory/filter_room_type_serde.rs b/crates/ruma-common/src/directory/filter_room_type_serde.rs new file mode 100644 index 00000000..8d36bd04 --- /dev/null +++ b/crates/ruma-common/src/directory/filter_room_type_serde.rs @@ -0,0 +1,24 @@ +use std::borrow::Cow; + +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +use super::RoomTypeFilter; + +impl Serialize for RoomTypeFilter { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_str().serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for RoomTypeFilter { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = Option::>::deserialize(deserializer)?; + Ok(s.into()) + } +} diff --git a/crates/ruma/Cargo.toml b/crates/ruma/Cargo.toml index 5d36a2ea..a14fb710 100644 --- a/crates/ruma/Cargo.toml +++ b/crates/ruma/Cargo.toml @@ -167,6 +167,7 @@ unstable-msc3554 = ["ruma-common/unstable-msc3554"] unstable-msc3618 = ["ruma-federation-api?/unstable-msc3618"] unstable-msc3723 = ["ruma-federation-api?/unstable-msc3723"] unstable-msc3786 = ["ruma-common/unstable-msc3786"] +unstable-msc3827 = ["ruma-common/unstable-msc3827"] # Private feature, only used in test / benchmarking code __ci = [ @@ -196,6 +197,7 @@ __ci = [ "unstable-msc3618", "unstable-msc3723", "unstable-msc3786", + "unstable-msc3827", ] [dependencies]