From bcc06ddd49cca6a8c22891f3f531e4c34e5c7755 Mon Sep 17 00:00:00 2001 From: iinuwa Date: Wed, 8 Apr 2020 16:49:59 -0500 Subject: [PATCH] Update public rooms endpoint --- CHANGELOG.md | 2 + src/r0/directory.rs | 4 +- src/r0/directory/get_public_rooms_filtered.rs | 190 +++++++++++++++++- 3 files changed, 193 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 57fed245..cb2d249c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ Breaking changes: * Remove `inhibit_login` request field, make `access_token` and `device_id` response fields optional (added in r0.4.0) * Remove deprecated `home_server` response field (removed in r0.4.0) * Add `auth_parameters` to `r0::account::AuthenticationData` +* Add `room_network` parameter to `r0::directory::get_public_rooms_filtered` to + represent `include_all_networks` and `third_party_instance_id` Matrix fields. Improvements: diff --git a/src/r0/directory.rs b/src/r0/directory.rs index bd5ebff9..a0bdcc1d 100644 --- a/src/r0/directory.rs +++ b/src/r0/directory.rs @@ -11,8 +11,8 @@ use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Deserialize, Serialize)] pub struct PublicRoomsChunk { /// Aliases of the room. - #[serde(skip_serializing_if = "Option::is_none")] - pub aliases: Option>, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub aliases: Vec, /// The canonical alias of the room, if any. #[serde(skip_serializing_if = "Option::is_none")] pub canonical_alias: Option, diff --git a/src/r0/directory/get_public_rooms_filtered.rs b/src/r0/directory/get_public_rooms_filtered.rs index 95d4d152..b3302f58 100644 --- a/src/r0/directory/get_public_rooms_filtered.rs +++ b/src/r0/directory/get_public_rooms_filtered.rs @@ -1,8 +1,15 @@ //! [POST /_matrix/client/r0/publicRooms](https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-publicrooms) +use std::fmt; + use js_int::UInt; use ruma_api::ruma_api; -use serde::{Deserialize, Serialize}; +use serde::{ + de::{MapAccess, Visitor}, + ser::SerializeStruct, + Deserialize, Deserializer, Serialize, Serializer, +}; +use serde_json::Value; use super::PublicRoomsChunk; @@ -32,6 +39,9 @@ ruma_api! { /// Filter to apply to the results. #[serde(skip_serializing_if = "Option::is_none")] pub filter: Option, + /// Network to fetch the public room lists from. + #[serde(flatten, skip_serializing_if = "crate::serde::is_default")] + pub room_network: RoomNetwork, } response { @@ -55,3 +65,181 @@ pub struct Filter { #[serde(skip_serializing_if = "Option::is_none")] pub generic_search_term: Option, } + +/// Information about which networks/protocols from application services on the +/// homeserver from which to request rooms. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum RoomNetwork { + /// Return rooms from the Matrix network. + Matrix, + /// Return rooms from all the networks/protocols the homeserver knows about. + All, + /// Return rooms from a specific third party network/protocol. + ThirdParty(String), +} + +impl Default for RoomNetwork { + fn default() -> Self { + RoomNetwork::Matrix + } +} + +impl Serialize for RoomNetwork { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state; + match self { + Self::Matrix => { + state = serializer.serialize_struct("RoomNetwork", 0)?; + } + Self::All => { + state = serializer.serialize_struct("RoomNetwork", 1)?; + state.serialize_field("include_all_networks", &true)?; + } + Self::ThirdParty(network) => { + state = serializer.serialize_struct("RoomNetwork", 1)?; + state.serialize_field("third_party_instance_id", network)?; + } + } + state.end() + } +} + +impl<'de> Deserialize<'de> for RoomNetwork { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_map(RoomNetworkVisitor) + } +} + +struct RoomNetworkVisitor; +impl<'de> Visitor<'de> for RoomNetworkVisitor { + type Value = RoomNetwork; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("Network selection") + } + + fn visit_map(self, mut access: M) -> Result + where + M: MapAccess<'de>, + { + let mut include_all_networks = false; + let mut third_party_instance_id = None; + while let Some((key, value)) = access.next_entry::()? { + match key.as_str() { + "include_all_networks" => { + include_all_networks = match value.as_bool() { + Some(b) => b, + _ => false, + } + } + "third_party_instance_id" => { + third_party_instance_id = value.as_str().map(|v| v.to_owned()) + } + _ => {} + }; + } + + if include_all_networks { + if third_party_instance_id.is_none() { + Ok(RoomNetwork::All) + } else { + Err(M::Error::custom( + "`include_all_networks = true` and `third_party_instance_id` are mutually exclusive.", + )) + } + } else { + Ok(match third_party_instance_id { + Some(network) => RoomNetwork::ThirdParty(network), + None => RoomNetwork::Matrix, + }) + } + } +} + +#[cfg(test)] +mod tests { + use serde_json::{from_value as from_json_value, json, to_value as to_json_value}; + + use super::RoomNetwork; + + #[test] + fn test_serialize_matrix_network_only() { + let json = json!({}); + assert_eq!(to_json_value(RoomNetwork::Matrix).unwrap(), json); + } + + #[test] + fn test_deserialize_matrix_network_only() { + let json = json!({ "include_all_networks": false }); + assert_eq!( + from_json_value::(json).unwrap(), + RoomNetwork::Matrix + ); + } + + #[test] + fn test_serialize_default_network_is_empty() { + let json = json!({}); + assert_eq!(to_json_value(RoomNetwork::default()).unwrap(), json); + } + + #[test] + fn test_deserialize_empty_network_is_default() { + let json = json!({}); + assert_eq!( + from_json_value::(json).unwrap(), + RoomNetwork::default() + ); + } + + #[test] + fn test_serialize_include_all_networks() { + let json = json!({ "include_all_networks": true }); + assert_eq!(to_json_value(RoomNetwork::All).unwrap(), json); + } + + #[test] + fn test_deserialize_include_all_networks() { + let json = json!({ "include_all_networks": true }); + assert_eq!( + from_json_value::(json).unwrap(), + RoomNetwork::All + ); + } + + #[test] + fn test_serialize_third_party_network() { + let json = json!({ "third_party_instance_id": "freenode" }); + assert_eq!( + to_json_value(RoomNetwork::ThirdParty("freenode".to_string())).unwrap(), + json + ); + } + + #[test] + fn test_deserialize_third_party_network() { + let json = json!({ "third_party_instance_id": "freenode" }); + assert_eq!( + from_json_value::(json).unwrap(), + RoomNetwork::ThirdParty("freenode".to_string()) + ); + } + + #[test] + fn test_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::(json) + .unwrap_err() + .to_string() + .as_str(), + "`include_all_networks = true` and `third_party_instance_id` are mutually exclusive." + ); + } +}