diff --git a/matrix_sdk/src/client.rs b/matrix_sdk/src/client.rs index 27d4c3ee..67612247 100644 --- a/matrix_sdk/src/client.rs +++ b/matrix_sdk/src/client.rs @@ -23,6 +23,7 @@ use std::result::Result as StdResult; use std::sync::Arc; use matrix_sdk_common::instant::{Duration, Instant}; +use matrix_sdk_common::js_int::UInt; use matrix_sdk_common::locks::RwLock; use matrix_sdk_common::uuid::Uuid; @@ -248,6 +249,8 @@ impl SyncSettings { } use api::r0::account::register; +use api::r0::directory::get_public_rooms; +use api::r0::directory::get_public_rooms_filtered; #[cfg(feature = "encryption")] use api::r0::keys::{claim_keys, get_keys, upload_keys, KeyAlgorithm}; use api::r0::membership::{ @@ -646,6 +649,94 @@ impl Client { self.send(request).await } + /// Search the homeserver's directory of public rooms. + /// + /// Returns a `get_public_rooms::Response`, an empty response. + /// + /// # Arguments + /// + /// * `limit` - The number of `PublicRoomsChunk`s in each response. + /// + /// * `since` - Pagination token from a previous request. + /// + /// * `server` - The name of the server, if `None` the requested server is used. + /// + /// # Examples + /// ```no_run + /// use matrix_sdk::{Client, RoomSearchBuilder}; + /// # use matrix_sdk::api::r0::directory::get_public_rooms; + /// # use url::Url; + /// # let homeserver = Url::parse("http://example.com").unwrap(); + /// # let limit = Some(10); + /// # let since = Some("since token"); + /// # let server = Some("server name"); + /// + /// let mut cli = Client::new(homeserver).unwrap(); + /// # use futures::executor::block_on; + /// # block_on(async { + /// assert!(cli.get_public_rooms( + /// limit, + /// since, + /// server + /// ).await.is_ok()); + /// # }); + /// ``` + pub async fn get_public_rooms( + &self, + limit: Option, + since: Option<&str>, + server: Option<&str>, + ) -> Result { + let limit = limit.map(|n| UInt::try_from(n).ok()).flatten(); + let since = since.map(ToString::to_string); + let server = server.map(ToString::to_string); + + let request = get_public_rooms::Request { + limit, + since, + server, + }; + self.send(request).await + } + + /// Search the homeserver's directory of public rooms filtered. + /// + /// Returns a `get_public_rooms_filtered::Response`, an empty response. + /// + /// # Arguments + /// + /// * `room_search` - The easiest way to create this request is using the `RoomSearchBuilder`. + /// + /// # Examples + /// ``` + /// # use std::convert::TryFrom; + /// # use matrix_sdk::{Client, RoomSearchBuilder}; + /// # use matrix_sdk::api::r0::directory::get_public_rooms_filtered::{self, RoomNetwork, Filter}; + /// # use url::Url; + /// # let homeserver = Url::parse("http://example.com").unwrap(); + /// # let mut rt = tokio::runtime::Runtime::new().unwrap(); + /// # rt.block_on(async { + /// # let last_sync_token = "".to_string(); + /// let mut client = Client::new(homeserver).unwrap(); + /// + /// let generic_search_term = Some("matrix-rust-sdk".to_string()); + /// let mut builder = RoomSearchBuilder::new(); + /// builder + /// .filter(Filter { generic_search_term, }) + /// .since(last_sync_token) + /// .room_network(RoomNetwork::Matrix); + /// + /// client.get_public_rooms_filtered(builder).await.is_err(); + /// # }) + /// ``` + pub async fn get_public_rooms_filtered>( + &self, + room_search: R, + ) -> Result { + let request = room_search.into(); + self.send(request).await + } + /// Create a room using the `RoomBuilder` and send the request. /// /// Sends a request to `/_matrix/client/r0/createRoom`, returns a `create_room::Response`, @@ -951,7 +1042,7 @@ impl Client { .body(body) .header(reqwest::header::CONTENT_TYPE, "application/json") } - method => panic!("Unsuported method {}", method), + method => panic!("Unsupported method {}", method), }; let request_builder = if requires_auth { @@ -1395,15 +1486,18 @@ impl Client { #[cfg(test)] mod test { use super::{ - api::r0::uiaa::AuthData, ban_user, create_receipt, create_typing_event, forget_room, - invite_user, kick_user, leave_room, register::RegistrationKind, set_read_marker, - Invite3pid, MessageEventContent, + api::r0::uiaa::AuthData, + ban_user, create_receipt, create_typing_event, forget_room, get_public_rooms, + get_public_rooms_filtered::{self, Filter}, + invite_user, kick_user, leave_room, + register::RegistrationKind, + set_read_marker, Invite3pid, MessageEventContent, }; use super::{Client, ClientConfig, Session, SyncSettings, Url}; use crate::events::collections::all::RoomEvent; use crate::events::room::message::TextMessageEventContent; use crate::identifiers::{EventId, RoomId, RoomIdOrAliasId, UserId}; - use crate::RegistrationBuilder; + use crate::{RegistrationBuilder, RoomSearchBuilder}; use matrix_sdk_base::JsonStore; use matrix_sdk_test::{test_json, EventBuilder, EventsJson}; @@ -1751,6 +1845,67 @@ mod test { {} } + #[tokio::test] + #[allow(irrefutable_let_patterns)] + async fn room_search_all() { + let homeserver = Url::from_str(&mockito::server_url()).unwrap(); + + let _m = mock( + "GET", + Matcher::Regex(r"^/_matrix/client/r0/publicRooms".to_string()), + ) + .with_status(200) + .with_body_from_file("../test_data/public_rooms.json") + .create(); + + let client = Client::new(homeserver).unwrap(); + + if let get_public_rooms::Response { chunk, .. } = client + // .get_public_rooms(Some(10), None, Some(&mockito::server_url().to_string())) + .get_public_rooms(Some(10), None, None) + .await + .unwrap() + { + assert_eq!(chunk.len(), 1) + } + } + + #[tokio::test] + #[allow(irrefutable_let_patterns)] + async fn room_search_filtered() { + let homeserver = Url::from_str(&mockito::server_url()).unwrap(); + let user = UserId::try_from("@example:localhost").unwrap(); + + let session = Session { + access_token: "1234".to_owned(), + user_id: user.clone(), + device_id: "DEVICEID".to_owned(), + }; + + let _m = mock( + "POST", + Matcher::Regex(r"^/_matrix/client/r0/publicRooms".to_string()), + ) + .with_status(200) + .with_body_from_file("../test_data/public_rooms.json") + .create(); + + let client = Client::new(homeserver).unwrap(); + client.restore_login(session).await.unwrap(); + + let generic_search_term = Some("cheese".to_string()); + let mut request = RoomSearchBuilder::default(); + request.filter(Filter { + generic_search_term, + }); + + if let get_public_rooms_filtered::Response { chunk, .. } = + client.get_public_rooms_filtered(request).await.unwrap() + { + assert_eq!(chunk.len(), 1) + } + } + #[tokio::test] #[allow(irrefutable_let_patterns)] async fn leave_room() { diff --git a/matrix_sdk/src/lib.rs b/matrix_sdk/src/lib.rs index b584c255..5b0757b5 100644 --- a/matrix_sdk/src/lib.rs +++ b/matrix_sdk/src/lib.rs @@ -52,7 +52,9 @@ mod error; mod request_builder; pub use client::{Client, ClientConfig, SyncSettings}; pub use error::{Error, Result}; -pub use request_builder::{MessagesRequestBuilder, RegistrationBuilder, RoomBuilder}; +pub use request_builder::{ + MessagesRequestBuilder, RegistrationBuilder, RoomBuilder, RoomSearchBuilder, +}; #[cfg(not(target_arch = "wasm32"))] pub(crate) const VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/matrix_sdk/src/request_builder.rs b/matrix_sdk/src/request_builder.rs index e43cea45..052f296f 100644 --- a/matrix_sdk/src/request_builder.rs +++ b/matrix_sdk/src/request_builder.rs @@ -4,6 +4,7 @@ use crate::events::EventJson; use crate::identifiers::{DeviceId, RoomId, UserId}; use api::r0::account::register; use api::r0::account::register::RegistrationKind; +use api::r0::directory::get_public_rooms_filtered::{self, Filter, RoomNetwork}; use api::r0::filter::RoomEventFilter; use api::r0::membership::Invite3pid; use api::r0::message::get_message_events::{self, Direction}; @@ -405,6 +406,92 @@ impl Into for RegistrationBuilder { } } +/// Create a builder for making get_message_event requests. +/// +/// # Examples +/// ``` +/// # use std::convert::TryFrom; +/// # use matrix_sdk::{Client, RoomSearchBuilder}; +/// # use matrix_sdk::api::r0::directory::get_public_rooms_filtered::{self, RoomNetwork, Filter}; +/// # use url::Url; +/// # let homeserver = Url::parse("http://example.com").unwrap(); +/// # let mut rt = tokio::runtime::Runtime::new().unwrap(); +/// # rt.block_on(async { +/// # let last_sync_token = "".to_string(); +/// let mut client = Client::new(homeserver).unwrap(); +/// +/// let generic_search_term = Some("matrix-rust-sdk".to_string()); +/// let mut builder = RoomSearchBuilder::new(); +/// builder +/// .filter(Filter { generic_search_term, }) +/// .since(last_sync_token) +/// .room_network(RoomNetwork::Matrix); +/// +/// client.get_public_rooms_filtered(builder).await.is_err(); +/// # }) +/// ``` +#[derive(Clone, Debug, Default)] +pub struct RoomSearchBuilder { + server: Option, + limit: Option, + since: Option, + filter: Option, + room_network: Option, +} + +impl RoomSearchBuilder { + /// Create a `RoomSearchBuilder` builder to make a `get_message_events::Request`. + /// + /// The `room_id` and `from`` fields **need to be set** to create the request. + pub fn new() -> Self { + Self::default() + } + + /// The server to fetch the public room lists from. + /// + /// `None` means the server this request is sent to. + pub fn server(&mut self, server: String) -> &mut Self { + self.server = Some(server); + self + } + + /// Limit for the number of results to return. + pub fn limit(&mut self, limit: UInt) -> &mut Self { + self.limit = Some(limit); + self + } + + /// Pagination token from a previous request. + pub fn since(&mut self, since: String) -> &mut Self { + self.since = Some(since); + self + } + + /// Filter to apply to the results. + pub fn filter(&mut self, filter: Filter) -> &mut Self { + self.filter = Some(filter); + self + } + + /// Network to fetch the public room lists from. + pub fn room_network(&mut self, room_network: RoomNetwork) -> &mut Self { + self.room_network = Some(room_network); + self + } +} + +impl Into for RoomSearchBuilder { + fn into(self) -> get_public_rooms_filtered::Request { + get_public_rooms_filtered::Request { + room_network: self.room_network.unwrap_or_default(), + limit: self.limit, + server: self.server, + since: self.since, + filter: self.filter, + } + } +} + #[cfg(test)] mod test { use std::collections::BTreeMap; diff --git a/test_data/public_rooms.json b/test_data/public_rooms.json new file mode 100644 index 00000000..2995bb3f --- /dev/null +++ b/test_data/public_rooms.json @@ -0,0 +1,19 @@ +{ + "chunk": [ + { + "aliases": [ + "#murrays:cheese.bar" + ], + "avatar_url": "mxc://bleeker.street/CHEDDARandBRIE", + "guest_can_join": false, + "name": "CHEESE", + "num_joined_members": 37, + "room_id": "!ol19s:bleecker.street", + "topic": "Tasty tasty cheese", + "world_readable": true + } + ], + "next_batch": "p190q", + "prev_batch": "p1902", + "total_room_count_estimate": 115 +}