use js_int::UInt;
use serde::{Deserialize, Serialize};
mod filter_room_type_serde;
mod room_network_serde;
use crate::{
room::RoomType, serde::StringEnum, OwnedMxcUri, OwnedRoomAliasId, OwnedRoomId, PrivOwnedStr,
};
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct PublicRoomsChunk {
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(
feature = "compat-empty-string-null",
serde(default, deserialize_with = "crate::serde::empty_string_as_none")
)]
pub canonical_alias: Option<OwnedRoomAliasId>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
pub num_joined_members: UInt,
pub room_id: OwnedRoomId,
#[serde(skip_serializing_if = "Option::is_none")]
pub topic: Option<String>,
pub world_readable: bool,
pub guest_can_join: bool,
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(
feature = "compat-empty-string-null",
serde(default, deserialize_with = "crate::serde::empty_string_as_none")
)]
pub avatar_url: Option<OwnedMxcUri>,
#[serde(default, skip_serializing_if = "crate::serde::is_default")]
pub join_rule: PublicRoomJoinRule,
#[serde(skip_serializing_if = "Option::is_none")]
pub room_type: Option<RoomType>,
}
#[derive(Debug)]
#[allow(clippy::exhaustive_structs)]
pub struct PublicRoomsChunkInit {
pub num_joined_members: UInt,
pub room_id: OwnedRoomId,
pub world_readable: bool,
pub guest_can_join: bool,
}
impl From<PublicRoomsChunkInit> for PublicRoomsChunk {
fn from(init: PublicRoomsChunkInit) -> Self {
let PublicRoomsChunkInit { num_joined_members, room_id, world_readable, guest_can_join } =
init;
Self {
canonical_alias: None,
name: None,
num_joined_members,
room_id,
topic: None,
world_readable,
guest_can_join,
avatar_url: None,
join_rule: PublicRoomJoinRule::default(),
room_type: None,
}
}
}
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct Filter {
#[serde(skip_serializing_if = "Option::is_none")]
pub generic_search_term: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
#[cfg_attr(feature = "compat-null", serde(deserialize_with = "crate::serde::none_as_default"))]
pub room_types: Vec<RoomTypeFilter>,
}
impl Filter {
pub fn new() -> Self {
Default::default()
}
pub fn is_empty(&self) -> bool {
self.generic_search_term.is_none()
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub enum RoomNetwork {
#[default]
Matrix,
All,
ThirdParty(String),
}
#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
#[derive(Clone, Default, PartialEq, Eq, StringEnum)]
#[ruma_enum(rename_all = "snake_case")]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub enum PublicRoomJoinRule {
Knock,
#[default]
Public,
#[doc(hidden)]
_Custom(PrivOwnedStr),
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum RoomTypeFilter {
Default,
Space,
#[doc(hidden)]
_Custom(PrivOwnedStr),
}
impl RoomTypeFilter {
pub fn as_str(&self) -> Option<&str> {
match self {
RoomTypeFilter::Default => None,
RoomTypeFilter::Space => Some("m.space"),
RoomTypeFilter::_Custom(s) => Some(&s.0),
}
}
}
impl<T> From<Option<T>> for RoomTypeFilter
where
T: AsRef<str> + Into<Box<str>>,
{
fn from(s: Option<T>) -> Self {
match s {
None => Self::Default,
Some(s) => match s.as_ref() {
"m.space" => Self::Space,
_ => Self::_Custom(PrivOwnedStr(s.into())),
},
}
}
}
impl From<Option<RoomType>> for RoomTypeFilter {
fn from(t: Option<RoomType>) -> Self {
match t {
None => Self::Default,
Some(s) => match s {
RoomType::Space => Self::Space,
_ => Self::from(Some(s.as_str())),
},
}
}
}
#[cfg(test)]
mod tests {
use assert_matches2::assert_matches;
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
use super::{Filter, RoomNetwork, RoomTypeFilter};
use crate::room::RoomType;
#[test]
fn test_from_room_type() {
let test = RoomType::Space;
let other: RoomTypeFilter = RoomTypeFilter::from(Some(test));
assert_eq!(other, RoomTypeFilter::Space);
}
#[test]
fn serialize_matrix_network_only() {
let json = json!({});
assert_eq!(to_json_value(RoomNetwork::Matrix).unwrap(), json);
}
#[test]
fn deserialize_matrix_network_only() {
let json = json!({ "include_all_networks": false });
assert_eq!(from_json_value::<RoomNetwork>(json).unwrap(), RoomNetwork::Matrix);
}
#[test]
fn serialize_default_network_is_empty() {
let json = json!({});
assert_eq!(to_json_value(RoomNetwork::default()).unwrap(), json);
}
#[test]
fn deserialize_empty_network_is_default() {
let json = json!({});
assert_eq!(from_json_value::<RoomNetwork>(json).unwrap(), RoomNetwork::Matrix);
}
#[test]
fn serialize_include_all_networks() {
let json = json!({ "include_all_networks": true });
assert_eq!(to_json_value(RoomNetwork::All).unwrap(), json);
}
#[test]
fn deserialize_include_all_networks() {
let json = json!({ "include_all_networks": true });
assert_eq!(from_json_value::<RoomNetwork>(json).unwrap(), RoomNetwork::All);
}
#[test]
fn serialize_third_party_network() {
let json = json!({ "third_party_instance_id": "freenode" });
assert_eq!(to_json_value(RoomNetwork::ThirdParty("freenode".to_owned())).unwrap(), json);
}
#[test]
fn deserialize_third_party_network() {
let json = json!({ "third_party_instance_id": "freenode" });
assert_eq!(
from_json_value::<RoomNetwork>(json).unwrap(),
RoomNetwork::ThirdParty("freenode".into())
);
}
#[test]
fn deserialize_include_all_networks_and_third_party_exclusivity() {
let json = json!({ "include_all_networks": true, "third_party_instance_id": "freenode" });
assert_eq!(
from_json_value::<RoomNetwork>(json).unwrap_err().to_string().as_str(),
"`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::<Filter>(json).unwrap();
assert_eq!(filter.generic_search_term, None);
assert_eq!(filter.room_types.len(), 0);
}
#[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!({ "room_types": [null, "m.space", "custom_type"] });
assert_eq!(to_json_value(filter).unwrap(), json);
}
#[test]
fn deserialize_filter_room_types() {
let json = json!({ "room_types": [null, "m.space", "custom_type"] });
let filter = from_json_value::<Filter>(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"));
}
}