state_store: load/store each room type (invite, join, left), add type for returning 3 room maps

master
Devin R 2020-05-11 15:32:58 -04:00
parent 22c4a1f2e7
commit 21712d0930
9 changed files with 220 additions and 121 deletions

View File

@ -3,7 +3,7 @@ use std::{env, process::exit};
use matrix_sdk::{ use matrix_sdk::{
self, self,
events::room::message::{MessageEvent, MessageEventContent, TextMessageEventContent}, events::room::message::{MessageEvent, MessageEventContent, TextMessageEventContent},
Client, ClientConfig, EventEmitter, JsonStore, RoomState, SyncSettings, Client, ClientConfig, EventEmitter, JsonStore, SyncRoom, SyncSettings,
}; };
use url::Url; use url::Url;
@ -21,8 +21,8 @@ impl CommandBot {
#[async_trait::async_trait] #[async_trait::async_trait]
impl EventEmitter for CommandBot { impl EventEmitter for CommandBot {
async fn on_room_message(&self, room: RoomState, event: &MessageEvent) { async fn on_room_message(&self, room: SyncRoom, event: &MessageEvent) {
if let RoomState::Joined(room) = room { if let SyncRoom::Joined(room) = room {
let msg_body = if let MessageEvent { let msg_body = if let MessageEvent {
content: MessageEventContent::Text(TextMessageEventContent { body: msg_body, .. }), content: MessageEventContent::Text(TextMessageEventContent { body: msg_body, .. }),
.. ..

View File

@ -4,15 +4,15 @@ use url::Url;
use matrix_sdk::{ use matrix_sdk::{
self, self,
events::room::message::{MessageEvent, MessageEventContent, TextMessageEventContent}, events::room::message::{MessageEvent, MessageEventContent, TextMessageEventContent},
Client, ClientConfig, EventEmitter, RoomState, SyncSettings, Client, ClientConfig, EventEmitter, SyncRoom, SyncSettings,
}; };
struct EventCallback; struct EventCallback;
#[async_trait::async_trait] #[async_trait::async_trait]
impl EventEmitter for EventCallback { impl EventEmitter for EventCallback {
async fn on_room_message(&self, room: RoomState, event: &MessageEvent) { async fn on_room_message(&self, room: SyncRoom, event: &MessageEvent) {
if let RoomState::Joined(room) = room { if let SyncRoom::Joined(room) = room {
if let MessageEvent { if let MessageEvent {
content: MessageEventContent::Text(TextMessageEventContent { body: msg_body, .. }), content: MessageEventContent::Text(TextMessageEventContent { body: msg_body, .. }),
sender, sender,

View File

@ -26,8 +26,8 @@
//! destroyed. //! destroyed.
#![deny(missing_docs)] #![deny(missing_docs)]
pub use matrix_sdk_base::{EventEmitter, Room, Session}; pub use matrix_sdk_base::{AllRooms, JsonStore, RoomState, StateStore};
pub use matrix_sdk_base::{JsonStore, RoomState, StateStore}; pub use matrix_sdk_base::{EventEmitter, Room, Session, SyncRoom};
pub use matrix_sdk_common::*; pub use matrix_sdk_common::*;
pub use reqwest::header::InvalidHeaderValue; pub use reqwest::header::InvalidHeaderValue;

View File

@ -36,7 +36,7 @@ use crate::events::EventJson;
use crate::identifiers::{RoomId, UserId}; use crate::identifiers::{RoomId, UserId};
use crate::models::Room; use crate::models::Room;
use crate::session::Session; use crate::session::Session;
use crate::state::{ClientState, StateStore}; use crate::state::{AllRooms, ClientState, StateStore};
use crate::EventEmitter; use crate::EventEmitter;
use std::ops::Deref; use std::ops::Deref;
@ -73,14 +73,15 @@ pub enum RoomStateType {
/// An enum that represents the state of the given `Room`. /// An enum that represents the state of the given `Room`.
/// ///
/// If the event came from the `join`, `invite` or `leave` rooms map from the server /// If the event came from the `join`, `invite` or `leave` rooms map from the server
/// the variant that holds the corresponding room is used. /// the variant that holds the corresponding room is used. `RoomState` is generic
pub enum RoomState { /// so it can be used to represent a `Room` or an `Arc<RwLock<Room>>`
pub enum RoomState<R> {
/// A room from the `join` section of a sync response. /// A room from the `join` section of a sync response.
Joined(Arc<RwLock<Room>>), Joined(R),
/// A room from the `leave` section of a sync response. /// A room from the `leave` section of a sync response.
Left(Arc<RwLock<Room>>), Left(R),
/// A room from the `invite` section of a sync response. /// A room from the `invite` section of a sync response.
Invited(Arc<RwLock<Room>>), Invited(R),
} }
/// A no IO Client implementation. /// A no IO Client implementation.
@ -229,8 +230,20 @@ impl BaseClient {
return Ok(false); return Ok(false);
} }
let mut rooms = store.load_all_rooms().await?; let AllRooms {
*self.joined_rooms.write().await = rooms mut joined,
mut invited,
mut left,
} = store.load_all_rooms().await?;
*self.joined_rooms.write().await = joined
.drain()
.map(|(k, room)| (k, Arc::new(RwLock::new(room))))
.collect();
*self.invited_rooms.write().await = invited
.drain()
.map(|(k, room)| (k, Arc::new(RwLock::new(room))))
.collect();
*self.left_rooms.write().await = left
.drain() .drain()
.map(|(k, room)| (k, Arc::new(RwLock::new(room)))) .map(|(k, room)| (k, Arc::new(RwLock::new(room))))
.collect(); .collect();
@ -248,7 +261,21 @@ impl BaseClient {
if let Some(store) = self.state_store.read().await.as_ref() { if let Some(store) = self.state_store.read().await.as_ref() {
if let Some(room) = self.get_joined_room(room_id).await { if let Some(room) = self.get_joined_room(room_id).await {
let room = room.read().await; let room = room.read().await;
store.store_room_state(room.deref()).await?; store
.store_room_state(RoomState::Joined(room.deref()))
.await?;
}
if let Some(room) = self.get_invited_room(room_id).await {
let room = room.read().await;
store
.store_room_state(RoomState::Invited(room.deref()))
.await?;
}
if let Some(room) = self.get_left_room(room_id).await {
let room = room.read().await;
store
.store_room_state(RoomState::Left(room.deref()))
.await?;
} }
} }
Ok(()) Ok(())
@ -754,7 +781,7 @@ impl BaseClient {
if updated { if updated {
if let Some(store) = self.state_store.read().await.as_ref() { if let Some(store) = self.state_store.read().await.as_ref() {
store store
.store_room_state(matrix_room.read().await.deref()) .store_room_state(RoomState::Joined(matrix_room.read().await.deref()))
.await?; .await?;
} }
} }
@ -801,7 +828,7 @@ impl BaseClient {
if updated { if updated {
if let Some(store) = self.state_store.read().await.as_ref() { if let Some(store) = self.state_store.read().await.as_ref() {
store store
.store_room_state(matrix_room.read().await.deref()) .store_room_state(RoomState::Left(matrix_room.read().await.deref()))
.await?; .await?;
} }
} }
@ -837,7 +864,7 @@ impl BaseClient {
if updated { if updated {
if let Some(store) = self.state_store.read().await.as_ref() { if let Some(store) = self.state_store.read().await.as_ref() {
store store
.store_room_state(matrix_room.read().await.deref()) .store_room_state(RoomState::Invited(matrix_room.read().await.deref()))
.await?; .await?;
} }
} }

View File

@ -12,6 +12,9 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use std::sync::Arc;
use tokio::sync::RwLock;
use crate::events::{ use crate::events::{
fully_read::FullyReadEvent, fully_read::FullyReadEvent,
@ -37,7 +40,10 @@ use crate::events::{
}, },
typing::TypingEvent, typing::TypingEvent,
}; };
use crate::RoomState; use crate::{Room, RoomState};
/// Type alias for `RoomState` enum when passed to `EventEmitter` methods.
pub type SyncRoom = RoomState<Arc<RwLock<Room>>>;
/// This trait allows any type implementing `EventEmitter` to specify event callbacks for each event. /// This trait allows any type implementing `EventEmitter` to specify event callbacks for each event.
/// The `AsyncClient` calls each method when the corresponding event is received. /// The `AsyncClient` calls each method when the corresponding event is received.
@ -52,7 +58,7 @@ use crate::RoomState;
/// # events::{ /// # events::{
/// # room::message::{MessageEvent, MessageEventContent, TextMessageEventContent}, /// # room::message::{MessageEvent, MessageEventContent, TextMessageEventContent},
/// # }, /// # },
/// # EventEmitter, RoomState /// # EventEmitter, SyncRoom
/// # }; /// # };
/// use tokio::sync::RwLock; /// use tokio::sync::RwLock;
/// ///
@ -60,8 +66,8 @@ use crate::RoomState;
/// ///
/// #[async_trait::async_trait] /// #[async_trait::async_trait]
/// impl EventEmitter for EventCallback { /// impl EventEmitter for EventCallback {
/// async fn on_room_message(&self, room: RoomState, event: &MessageEvent) { /// async fn on_room_message(&self, room: SyncRoom, event: &MessageEvent) {
/// if let RoomState::Joined(room) = room { /// if let SyncRoom::Joined(room) = room {
/// if let MessageEvent { /// if let MessageEvent {
/// content: MessageEventContent::Text(TextMessageEventContent { body: msg_body, .. }), /// content: MessageEventContent::Text(TextMessageEventContent { body: msg_body, .. }),
/// sender, /// sender,
@ -87,82 +93,78 @@ use crate::RoomState;
pub trait EventEmitter: Send + Sync { pub trait EventEmitter: Send + Sync {
// ROOM EVENTS from `IncomingTimeline` // ROOM EVENTS from `IncomingTimeline`
/// Fires when `AsyncClient` receives a `RoomEvent::RoomMember` event. /// Fires when `AsyncClient` receives a `RoomEvent::RoomMember` event.
async fn on_room_member(&self, _: RoomState, _: &MemberEvent) {} async fn on_room_member(&self, _: SyncRoom, _: &MemberEvent) {}
/// Fires when `AsyncClient` receives a `RoomEvent::RoomName` event. /// Fires when `AsyncClient` receives a `RoomEvent::RoomName` event.
async fn on_room_name(&self, _: RoomState, _: &NameEvent) {} async fn on_room_name(&self, _: SyncRoom, _: &NameEvent) {}
/// Fires when `AsyncClient` receives a `RoomEvent::RoomCanonicalAlias` event. /// Fires when `AsyncClient` receives a `RoomEvent::RoomCanonicalAlias` event.
async fn on_room_canonical_alias(&self, _: RoomState, _: &CanonicalAliasEvent) {} async fn on_room_canonical_alias(&self, _: SyncRoom, _: &CanonicalAliasEvent) {}
/// Fires when `AsyncClient` receives a `RoomEvent::RoomAliases` event. /// Fires when `AsyncClient` receives a `RoomEvent::RoomAliases` event.
async fn on_room_aliases(&self, _: RoomState, _: &AliasesEvent) {} async fn on_room_aliases(&self, _: SyncRoom, _: &AliasesEvent) {}
/// Fires when `AsyncClient` receives a `RoomEvent::RoomAvatar` event. /// Fires when `AsyncClient` receives a `RoomEvent::RoomAvatar` event.
async fn on_room_avatar(&self, _: RoomState, _: &AvatarEvent) {} async fn on_room_avatar(&self, _: SyncRoom, _: &AvatarEvent) {}
/// Fires when `AsyncClient` receives a `RoomEvent::RoomMessage` event. /// Fires when `AsyncClient` receives a `RoomEvent::RoomMessage` event.
async fn on_room_message(&self, _: RoomState, _: &MessageEvent) {} async fn on_room_message(&self, _: SyncRoom, _: &MessageEvent) {}
/// Fires when `AsyncClient` receives a `RoomEvent::RoomMessageFeedback` event. /// Fires when `AsyncClient` receives a `RoomEvent::RoomMessageFeedback` event.
async fn on_room_message_feedback(&self, _: RoomState, _: &FeedbackEvent) {} async fn on_room_message_feedback(&self, _: SyncRoom, _: &FeedbackEvent) {}
/// Fires when `AsyncClient` receives a `RoomEvent::RoomRedaction` event. /// Fires when `AsyncClient` receives a `RoomEvent::RoomRedaction` event.
async fn on_room_redaction(&self, _: RoomState, _: &RedactionEvent) {} async fn on_room_redaction(&self, _: SyncRoom, _: &RedactionEvent) {}
/// Fires when `AsyncClient` receives a `RoomEvent::RoomPowerLevels` event. /// Fires when `AsyncClient` receives a `RoomEvent::RoomPowerLevels` event.
async fn on_room_power_levels(&self, _: RoomState, _: &PowerLevelsEvent) {} async fn on_room_power_levels(&self, _: SyncRoom, _: &PowerLevelsEvent) {}
/// Fires when `AsyncClient` receives a `RoomEvent::Tombstone` event. /// Fires when `AsyncClient` receives a `RoomEvent::Tombstone` event.
async fn on_room_tombstone(&self, _: RoomState, _: &TombstoneEvent) {} async fn on_room_tombstone(&self, _: SyncRoom, _: &TombstoneEvent) {}
// `RoomEvent`s from `IncomingState` // `RoomEvent`s from `IncomingState`
/// Fires when `AsyncClient` receives a `StateEvent::RoomMember` event. /// Fires when `AsyncClient` receives a `StateEvent::RoomMember` event.
async fn on_state_member(&self, _: RoomState, _: &MemberEvent) {} async fn on_state_member(&self, _: SyncRoom, _: &MemberEvent) {}
/// Fires when `AsyncClient` receives a `StateEvent::RoomName` event. /// Fires when `AsyncClient` receives a `StateEvent::RoomName` event.
async fn on_state_name(&self, _: RoomState, _: &NameEvent) {} async fn on_state_name(&self, _: SyncRoom, _: &NameEvent) {}
/// Fires when `AsyncClient` receives a `StateEvent::RoomCanonicalAlias` event. /// Fires when `AsyncClient` receives a `StateEvent::RoomCanonicalAlias` event.
async fn on_state_canonical_alias(&self, _: RoomState, _: &CanonicalAliasEvent) {} async fn on_state_canonical_alias(&self, _: SyncRoom, _: &CanonicalAliasEvent) {}
/// Fires when `AsyncClient` receives a `StateEvent::RoomAliases` event. /// Fires when `AsyncClient` receives a `StateEvent::RoomAliases` event.
async fn on_state_aliases(&self, _: RoomState, _: &AliasesEvent) {} async fn on_state_aliases(&self, _: SyncRoom, _: &AliasesEvent) {}
/// Fires when `AsyncClient` receives a `StateEvent::RoomAvatar` event. /// Fires when `AsyncClient` receives a `StateEvent::RoomAvatar` event.
async fn on_state_avatar(&self, _: RoomState, _: &AvatarEvent) {} async fn on_state_avatar(&self, _: SyncRoom, _: &AvatarEvent) {}
/// Fires when `AsyncClient` receives a `StateEvent::RoomPowerLevels` event. /// Fires when `AsyncClient` receives a `StateEvent::RoomPowerLevels` event.
async fn on_state_power_levels(&self, _: RoomState, _: &PowerLevelsEvent) {} async fn on_state_power_levels(&self, _: SyncRoom, _: &PowerLevelsEvent) {}
/// Fires when `AsyncClient` receives a `StateEvent::RoomJoinRules` event. /// Fires when `AsyncClient` receives a `StateEvent::RoomJoinRules` event.
async fn on_state_join_rules(&self, _: RoomState, _: &JoinRulesEvent) {} async fn on_state_join_rules(&self, _: SyncRoom, _: &JoinRulesEvent) {}
// `AnyStrippedStateEvent`s // `AnyStrippedStateEvent`s
/// Fires when `AsyncClient` receives a `AnyStrippedStateEvent::StrippedRoomMember` event. /// Fires when `AsyncClient` receives a `AnyStrippedStateEvent::StrippedRoomMember` event.
async fn on_stripped_state_member(&self, _: RoomState, _: &StrippedRoomMember) {} async fn on_stripped_state_member(&self, _: SyncRoom, _: &StrippedRoomMember) {}
/// Fires when `AsyncClient` receives a `AnyStrippedStateEvent::StrippedRoomName` event. /// Fires when `AsyncClient` receives a `AnyStrippedStateEvent::StrippedRoomName` event.
async fn on_stripped_state_name(&self, _: RoomState, _: &StrippedRoomName) {} async fn on_stripped_state_name(&self, _: SyncRoom, _: &StrippedRoomName) {}
/// Fires when `AsyncClient` receives a `AnyStrippedStateEvent::StrippedRoomCanonicalAlias` event. /// Fires when `AsyncClient` receives a `AnyStrippedStateEvent::StrippedRoomCanonicalAlias` event.
async fn on_stripped_state_canonical_alias( async fn on_stripped_state_canonical_alias(&self, _: SyncRoom, _: &StrippedRoomCanonicalAlias) {
&self,
_: RoomState,
_: &StrippedRoomCanonicalAlias,
) {
} }
/// Fires when `AsyncClient` receives a `AnyStrippedStateEvent::StrippedRoomAliases` event. /// Fires when `AsyncClient` receives a `AnyStrippedStateEvent::StrippedRoomAliases` event.
async fn on_stripped_state_aliases(&self, _: RoomState, _: &StrippedRoomAliases) {} async fn on_stripped_state_aliases(&self, _: SyncRoom, _: &StrippedRoomAliases) {}
/// Fires when `AsyncClient` receives a `AnyStrippedStateEvent::StrippedRoomAvatar` event. /// Fires when `AsyncClient` receives a `AnyStrippedStateEvent::StrippedRoomAvatar` event.
async fn on_stripped_state_avatar(&self, _: RoomState, _: &StrippedRoomAvatar) {} async fn on_stripped_state_avatar(&self, _: SyncRoom, _: &StrippedRoomAvatar) {}
/// Fires when `AsyncClient` receives a `AnyStrippedStateEvent::StrippedRoomPowerLevels` event. /// Fires when `AsyncClient` receives a `AnyStrippedStateEvent::StrippedRoomPowerLevels` event.
async fn on_stripped_state_power_levels(&self, _: RoomState, _: &StrippedRoomPowerLevels) {} async fn on_stripped_state_power_levels(&self, _: SyncRoom, _: &StrippedRoomPowerLevels) {}
/// Fires when `AsyncClient` receives a `AnyStrippedStateEvent::StrippedRoomJoinRules` event. /// Fires when `AsyncClient` receives a `AnyStrippedStateEvent::StrippedRoomJoinRules` event.
async fn on_stripped_state_join_rules(&self, _: RoomState, _: &StrippedRoomJoinRules) {} async fn on_stripped_state_join_rules(&self, _: SyncRoom, _: &StrippedRoomJoinRules) {}
// `NonRoomEvent` (this is a type alias from ruma_events) // `NonRoomEvent` (this is a type alias from ruma_events)
/// Fires when `AsyncClient` receives a `NonRoomEvent::RoomMember` event. /// Fires when `AsyncClient` receives a `NonRoomEvent::RoomMember` event.
async fn on_account_presence(&self, _: RoomState, _: &PresenceEvent) {} async fn on_account_presence(&self, _: SyncRoom, _: &PresenceEvent) {}
/// Fires when `AsyncClient` receives a `NonRoomEvent::RoomName` event. /// Fires when `AsyncClient` receives a `NonRoomEvent::RoomName` event.
async fn on_account_ignored_users(&self, _: RoomState, _: &IgnoredUserListEvent) {} async fn on_account_ignored_users(&self, _: SyncRoom, _: &IgnoredUserListEvent) {}
/// Fires when `AsyncClient` receives a `NonRoomEvent::RoomCanonicalAlias` event. /// Fires when `AsyncClient` receives a `NonRoomEvent::RoomCanonicalAlias` event.
async fn on_account_push_rules(&self, _: RoomState, _: &PushRulesEvent) {} async fn on_account_push_rules(&self, _: SyncRoom, _: &PushRulesEvent) {}
/// Fires when `AsyncClient` receives a `NonRoomEvent::RoomAliases` event. /// Fires when `AsyncClient` receives a `NonRoomEvent::RoomAliases` event.
async fn on_account_data_fully_read(&self, _: RoomState, _: &FullyReadEvent) {} async fn on_account_data_fully_read(&self, _: SyncRoom, _: &FullyReadEvent) {}
/// Fires when `AsyncClient` receives a `NonRoomEvent::Typing` event. /// Fires when `AsyncClient` receives a `NonRoomEvent::Typing` event.
async fn on_account_data_typing(&self, _: RoomState, _: &TypingEvent) {} async fn on_account_data_typing(&self, _: SyncRoom, _: &TypingEvent) {}
/// Fires when `AsyncClient` receives a `NonRoomEvent::Receipt` event. /// Fires when `AsyncClient` receives a `NonRoomEvent::Receipt` event.
/// ///
/// This is always a read receipt. /// This is always a read receipt.
async fn on_account_data_receipt(&self, _: RoomState, _: &ReceiptEvent) {} async fn on_account_data_receipt(&self, _: SyncRoom, _: &ReceiptEvent) {}
// `PresenceEvent` is a struct so there is only the one method // `PresenceEvent` is a struct so there is only the one method
/// Fires when `AsyncClient` receives a `NonRoomEvent::RoomAliases` event. /// Fires when `AsyncClient` receives a `NonRoomEvent::RoomAliases` event.
async fn on_presence_event(&self, _: RoomState, _: &PresenceEvent) {} async fn on_presence_event(&self, _: SyncRoom, _: &PresenceEvent) {}
} }
#[cfg(test)] #[cfg(test)]
@ -174,72 +176,74 @@ mod test {
pub struct EvEmitterTest(Arc<Mutex<Vec<String>>>); pub struct EvEmitterTest(Arc<Mutex<Vec<String>>>);
#[async_trait::async_trait] #[async_trait::async_trait]
// we don't need to test our tests right?
#[cfg_attr(tarpaulin, skip)]
impl EventEmitter for EvEmitterTest { impl EventEmitter for EvEmitterTest {
async fn on_room_member(&self, _: RoomState, _: &MemberEvent) { async fn on_room_member(&self, _: SyncRoom, _: &MemberEvent) {
self.0.lock().await.push("member".to_string()) self.0.lock().await.push("member".to_string())
} }
async fn on_room_name(&self, _: RoomState, _: &NameEvent) { async fn on_room_name(&self, _: SyncRoom, _: &NameEvent) {
self.0.lock().await.push("name".to_string()) self.0.lock().await.push("name".to_string())
} }
async fn on_room_canonical_alias(&self, _: RoomState, _: &CanonicalAliasEvent) { async fn on_room_canonical_alias(&self, _: SyncRoom, _: &CanonicalAliasEvent) {
self.0.lock().await.push("canonical".to_string()) self.0.lock().await.push("canonical".to_string())
} }
async fn on_room_aliases(&self, _: RoomState, _: &AliasesEvent) { async fn on_room_aliases(&self, _: SyncRoom, _: &AliasesEvent) {
self.0.lock().await.push("aliases".to_string()) self.0.lock().await.push("aliases".to_string())
} }
async fn on_room_avatar(&self, _: RoomState, _: &AvatarEvent) { async fn on_room_avatar(&self, _: SyncRoom, _: &AvatarEvent) {
self.0.lock().await.push("avatar".to_string()) self.0.lock().await.push("avatar".to_string())
} }
async fn on_room_message(&self, _: RoomState, _: &MessageEvent) { async fn on_room_message(&self, _: SyncRoom, _: &MessageEvent) {
self.0.lock().await.push("message".to_string()) self.0.lock().await.push("message".to_string())
} }
async fn on_room_message_feedback(&self, _: RoomState, _: &FeedbackEvent) { async fn on_room_message_feedback(&self, _: SyncRoom, _: &FeedbackEvent) {
self.0.lock().await.push("feedback".to_string()) self.0.lock().await.push("feedback".to_string())
} }
async fn on_room_redaction(&self, _: RoomState, _: &RedactionEvent) { async fn on_room_redaction(&self, _: SyncRoom, _: &RedactionEvent) {
self.0.lock().await.push("redaction".to_string()) self.0.lock().await.push("redaction".to_string())
} }
async fn on_room_power_levels(&self, _: RoomState, _: &PowerLevelsEvent) { async fn on_room_power_levels(&self, _: SyncRoom, _: &PowerLevelsEvent) {
self.0.lock().await.push("power".to_string()) self.0.lock().await.push("power".to_string())
} }
async fn on_room_tombstone(&self, _: RoomState, _: &TombstoneEvent) { async fn on_room_tombstone(&self, _: SyncRoom, _: &TombstoneEvent) {
self.0.lock().await.push("tombstone".to_string()) self.0.lock().await.push("tombstone".to_string())
} }
async fn on_state_member(&self, _: RoomState, _: &MemberEvent) { async fn on_state_member(&self, _: SyncRoom, _: &MemberEvent) {
self.0.lock().await.push("state member".to_string()) self.0.lock().await.push("state member".to_string())
} }
async fn on_state_name(&self, _: RoomState, _: &NameEvent) { async fn on_state_name(&self, _: SyncRoom, _: &NameEvent) {
self.0.lock().await.push("state name".to_string()) self.0.lock().await.push("state name".to_string())
} }
async fn on_state_canonical_alias(&self, _: RoomState, _: &CanonicalAliasEvent) { async fn on_state_canonical_alias(&self, _: SyncRoom, _: &CanonicalAliasEvent) {
self.0.lock().await.push("state canonical".to_string()) self.0.lock().await.push("state canonical".to_string())
} }
async fn on_state_aliases(&self, _: RoomState, _: &AliasesEvent) { async fn on_state_aliases(&self, _: SyncRoom, _: &AliasesEvent) {
self.0.lock().await.push("state aliases".to_string()) self.0.lock().await.push("state aliases".to_string())
} }
async fn on_state_avatar(&self, _: RoomState, _: &AvatarEvent) { async fn on_state_avatar(&self, _: SyncRoom, _: &AvatarEvent) {
self.0.lock().await.push("state avatar".to_string()) self.0.lock().await.push("state avatar".to_string())
} }
async fn on_state_power_levels(&self, _: RoomState, _: &PowerLevelsEvent) { async fn on_state_power_levels(&self, _: SyncRoom, _: &PowerLevelsEvent) {
self.0.lock().await.push("state power".to_string()) self.0.lock().await.push("state power".to_string())
} }
async fn on_state_join_rules(&self, _: RoomState, _: &JoinRulesEvent) { async fn on_state_join_rules(&self, _: SyncRoom, _: &JoinRulesEvent) {
self.0.lock().await.push("state rules".to_string()) self.0.lock().await.push("state rules".to_string())
} }
async fn on_stripped_state_member(&self, _: RoomState, _: &StrippedRoomMember) { async fn on_stripped_state_member(&self, _: SyncRoom, _: &StrippedRoomMember) {
self.0 self.0
.lock() .lock()
.await .await
.push("stripped state member".to_string()) .push("stripped state member".to_string())
} }
async fn on_stripped_state_name(&self, _: RoomState, _: &StrippedRoomName) { async fn on_stripped_state_name(&self, _: SyncRoom, _: &StrippedRoomName) {
self.0.lock().await.push("stripped state name".to_string()) self.0.lock().await.push("stripped state name".to_string())
} }
async fn on_stripped_state_canonical_alias( async fn on_stripped_state_canonical_alias(
&self, &self,
_: RoomState, _: SyncRoom,
_: &StrippedRoomCanonicalAlias, _: &StrippedRoomCanonicalAlias,
) { ) {
self.0 self.0
@ -247,38 +251,38 @@ mod test {
.await .await
.push("stripped state canonical".to_string()) .push("stripped state canonical".to_string())
} }
async fn on_stripped_state_aliases(&self, _: RoomState, _: &StrippedRoomAliases) { async fn on_stripped_state_aliases(&self, _: SyncRoom, _: &StrippedRoomAliases) {
self.0 self.0
.lock() .lock()
.await .await
.push("stripped state aliases".to_string()) .push("stripped state aliases".to_string())
} }
async fn on_stripped_state_avatar(&self, _: RoomState, _: &StrippedRoomAvatar) { async fn on_stripped_state_avatar(&self, _: SyncRoom, _: &StrippedRoomAvatar) {
self.0 self.0
.lock() .lock()
.await .await
.push("stripped state avatar".to_string()) .push("stripped state avatar".to_string())
} }
async fn on_stripped_state_power_levels(&self, _: RoomState, _: &StrippedRoomPowerLevels) { async fn on_stripped_state_power_levels(&self, _: SyncRoom, _: &StrippedRoomPowerLevels) {
self.0.lock().await.push("stripped state power".to_string()) self.0.lock().await.push("stripped state power".to_string())
} }
async fn on_stripped_state_join_rules(&self, _: RoomState, _: &StrippedRoomJoinRules) { async fn on_stripped_state_join_rules(&self, _: SyncRoom, _: &StrippedRoomJoinRules) {
self.0.lock().await.push("stripped state rules".to_string()) self.0.lock().await.push("stripped state rules".to_string())
} }
async fn on_account_presence(&self, _: RoomState, _: &PresenceEvent) { async fn on_account_presence(&self, _: SyncRoom, _: &PresenceEvent) {
self.0.lock().await.push("account presence".to_string()) self.0.lock().await.push("account presence".to_string())
} }
async fn on_account_ignored_users(&self, _: RoomState, _: &IgnoredUserListEvent) { async fn on_account_ignored_users(&self, _: SyncRoom, _: &IgnoredUserListEvent) {
self.0.lock().await.push("account ignore".to_string()) self.0.lock().await.push("account ignore".to_string())
} }
async fn on_account_push_rules(&self, _: RoomState, _: &PushRulesEvent) { async fn on_account_push_rules(&self, _: SyncRoom, _: &PushRulesEvent) {
self.0.lock().await.push("account push rules".to_string()) self.0.lock().await.push("account push rules".to_string())
} }
async fn on_account_data_fully_read(&self, _: RoomState, _: &FullyReadEvent) { async fn on_account_data_fully_read(&self, _: SyncRoom, _: &FullyReadEvent) {
self.0.lock().await.push("account read".to_string()) self.0.lock().await.push("account read".to_string())
} }
async fn on_presence_event(&self, _: RoomState, _: &PresenceEvent) { async fn on_presence_event(&self, _: SyncRoom, _: &PresenceEvent) {
self.0.lock().await.push("presence event".to_string()) self.0.lock().await.push("presence event".to_string())
} }
} }

View File

@ -37,8 +37,8 @@ mod session;
mod state; mod state;
pub use client::{BaseClient, RoomState, RoomStateType}; pub use client::{BaseClient, RoomState, RoomStateType};
pub use event_emitter::EventEmitter; pub use event_emitter::{EventEmitter, SyncRoom};
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
pub use matrix_sdk_crypto::{Device, TrustState}; pub use matrix_sdk_crypto::{Device, TrustState};
pub use models::Room; pub use models::Room;
pub use state::{JsonStore, StateStore}; pub use state::{AllRooms, JsonStore, StateStore};

View File

@ -167,7 +167,9 @@ mod test {
let mut joined_rooms = HashMap::new(); let mut joined_rooms = HashMap::new();
joined_rooms.insert(id, room); joined_rooms.insert(id, room);
println!("{}", serde_json::to_string_pretty(&joined_rooms).unwrap());
// println!("{}", serde_json::to_string_pretty(&joined_rooms).unwrap());
// this is the correct JSON string changes to `ruma-events` have not been released // this is the correct JSON string changes to `ruma-events` have not been released
// that would fix the doubling of fields // that would fix the doubling of fields
// TODO uncomment when fixed // TODO uncomment when fixed

View File

@ -14,15 +14,14 @@
// limitations under the License. // limitations under the License.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::HashMap;
pub mod state_store; pub mod state_store;
pub use state_store::JsonStore; pub use state_store::{AllRooms, JsonStore};
use crate::client::{BaseClient, Token}; use crate::client::{BaseClient, Token};
use crate::events::push_rules::Ruleset; use crate::events::push_rules::Ruleset;
use crate::identifiers::{RoomId, UserId}; use crate::identifiers::UserId;
use crate::{Result, Room, Session}; use crate::{Result, Room, RoomState, Session};
/// `ClientState` holds all the information to restore a `BaseClient` /// `ClientState` holds all the information to restore a `BaseClient`
/// except the `access_token` as the default store is not secure. /// except the `access_token` as the default store is not secure.
@ -73,11 +72,11 @@ pub trait StateStore: Send + Sync {
/// Load the state of all `Room`s. /// Load the state of all `Room`s.
/// ///
/// This will be mapped over in the client in order to store `Room`s in an async safe way. /// This will be mapped over in the client in order to store `Room`s in an async safe way.
async fn load_all_rooms(&self) -> Result<HashMap<RoomId, Room>>; async fn load_all_rooms(&self) -> Result<AllRooms>;
/// Save the current state of the `BaseClient` using the `StateStore::Store` type. /// Save the current state of the `BaseClient` using the `StateStore::Store` type.
async fn store_client_state(&self, _: ClientState) -> Result<()>; async fn store_client_state(&self, _: ClientState) -> Result<()>;
/// Save the state a single `Room`. /// Save the state a single `Room`.
async fn store_room_state(&self, _: &Room) -> Result<()>; async fn store_room_state(&self, _: RoomState<&Room>) -> Result<()>;
} }
#[cfg(test)] #[cfg(test)]
@ -87,6 +86,8 @@ mod test {
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::TryFrom; use std::convert::TryFrom;
use crate::identifiers::RoomId;
#[test] #[test]
fn serialize() { fn serialize() {
let id = RoomId::try_from("!roomid:example.com").unwrap(); let id = RoomId::try_from("!roomid:example.com").unwrap();

View File

@ -12,7 +12,20 @@ use tokio::sync::RwLock;
use super::{ClientState, StateStore}; use super::{ClientState, StateStore};
use crate::identifiers::RoomId; use crate::identifiers::RoomId;
use crate::{Error, Result, Room, Session}; use crate::{Error, Result, Room, RoomState, Session};
/// `JsonStore::load_all_rooms` returns `AllRooms`.
///
/// `AllRooms` is made of the `joined`, `invited` and `left` room maps.
pub struct AllRooms {
/// The joined room mapping of `RoomId` to `Room`.
pub joined: HashMap<RoomId, Room>,
/// The invited room mapping of `RoomId` to `Room`.
pub invited: HashMap<RoomId, Room>,
/// The left room mapping of `RoomId` to `Room`.
pub left: HashMap<RoomId, Room>,
}
/// A default `StateStore` implementation that serializes state as json /// A default `StateStore` implementation that serializes state as json
/// and saves it to disk. /// and saves it to disk.
/// ///
@ -60,11 +73,21 @@ impl StateStore for JsonStore {
} }
} }
async fn load_all_rooms(&self) -> Result<HashMap<RoomId, Room>> { async fn load_all_rooms(&self) -> Result<AllRooms> {
let mut path = self.path.read().await.clone(); let mut path = self.path.read().await.clone();
path.push("rooms"); path.push("rooms");
let mut rooms_map = HashMap::new(); let mut joined = HashMap::new();
let mut left = HashMap::new();
let mut invited = HashMap::new();
for room_state_type in &["joined", "invited", "left"] {
path.push(room_state_type);
// don't load rooms that aren't saved yet
if !path.exists() {
path.pop();
continue;
}
for file in fs::read_dir(&path)? { for file in fs::read_dir(&path)? {
let file = file?.path(); let file = file?.path();
@ -77,17 +100,28 @@ impl StateStore for JsonStore {
let room = serde_json::from_str::<Room>(&json).map_err(Error::from)?; let room = serde_json::from_str::<Room>(&json).map_err(Error::from)?;
let room_id = room.room_id.clone(); let room_id = room.room_id.clone();
rooms_map.insert(room_id, room); match *room_state_type {
"joined" => joined.insert(room_id, room),
"invited" => invited.insert(room_id, room),
"left" => left.insert(room_id, room),
_ => unreachable!("an array with 3 const elements was altered in JsonStore"),
};
}
path.pop();
} }
Ok(rooms_map) Ok(AllRooms {
joined,
left,
invited,
})
} }
async fn store_client_state(&self, state: ClientState) -> Result<()> { async fn store_client_state(&self, state: ClientState) -> Result<()> {
let mut path = self.path.read().await.clone(); let mut path = self.path.read().await.clone();
path.push("client.json"); path.push("client.json");
if !Path::new(&path).exists() { if !path.exists() {
let mut dir = path.clone(); let mut dir = path.clone();
dir.pop(); dir.pop();
async_fs::create_dir_all(dir).await?; async_fs::create_dir_all(dir).await?;
@ -104,16 +138,23 @@ impl StateStore for JsonStore {
file.write_all(json.as_bytes()).await.map_err(Error::from) file.write_all(json.as_bytes()).await.map_err(Error::from)
} }
async fn store_room_state(&self, room: &Room) -> Result<()> { async fn store_room_state(&self, room: RoomState<&Room>) -> Result<()> {
let (room, room_state) = match room {
RoomState::Joined(room) => (room, "joined"),
RoomState::Invited(room) => (room, "invited"),
RoomState::Left(room) => (room, "left"),
};
if !self.user_path_set.load(Ordering::SeqCst) { if !self.user_path_set.load(Ordering::SeqCst) {
self.user_path_set.swap(true, Ordering::SeqCst); self.user_path_set.swap(true, Ordering::SeqCst);
self.path.write().await.push(room.own_user_id.localpart()) self.path.write().await.push(room.own_user_id.localpart())
} }
let mut path = self.path.read().await.clone(); let mut path = self.path.read().await.clone();
path.push(&format!("rooms/{}.json", room.room_id)); path.push("rooms");
path.push(&format!("{}/{}.json", room_state, room.room_id));
if !Path::new(&path).exists() { if !path.exists() {
let mut dir = path.clone(); let mut dir = path.clone();
dir.pop(); dir.pop();
async_fs::create_dir_all(dir).await?; async_fs::create_dir_all(dir).await?;
@ -187,7 +228,7 @@ mod test {
} }
#[tokio::test] #[tokio::test]
async fn test_store_room_state() { async fn test_store_load_joined_room_state() {
let dir = tempdir().unwrap(); let dir = tempdir().unwrap();
let path: &Path = dir.path(); let path: &Path = dir.path();
let store = JsonStore::open(path).unwrap(); let store = JsonStore::open(path).unwrap();
@ -196,13 +237,16 @@ mod test {
let user = UserId::try_from("@example:example.com").unwrap(); let user = UserId::try_from("@example:example.com").unwrap();
let room = Room::new(&id, &user); let room = Room::new(&id, &user);
store.store_room_state(&room).await.unwrap(); store
let loaded = store.load_all_rooms().await.unwrap(); .store_room_state(RoomState::Joined(&room))
assert_eq!(loaded.get(&id), Some(&Room::new(&id, &user))); .await
.unwrap();
let AllRooms { joined, .. } = store.load_all_rooms().await.unwrap();
assert_eq!(joined.get(&id), Some(&Room::new(&id, &user)));
} }
#[tokio::test] #[tokio::test]
async fn test_load_rooms() { async fn test_store_load_left_room_state() {
let dir = tempdir().unwrap(); let dir = tempdir().unwrap();
let path: &Path = dir.path(); let path: &Path = dir.path();
let store = JsonStore::open(path).unwrap(); let store = JsonStore::open(path).unwrap();
@ -211,9 +255,30 @@ mod test {
let user = UserId::try_from("@example:example.com").unwrap(); let user = UserId::try_from("@example:example.com").unwrap();
let room = Room::new(&id, &user); let room = Room::new(&id, &user);
store.store_room_state(&room).await.unwrap(); store
let loaded = store.load_all_rooms().await.unwrap(); .store_room_state(RoomState::Left(&room))
assert_eq!(&room, loaded.get(&id).unwrap()); .await
.unwrap();
let AllRooms { left, .. } = store.load_all_rooms().await.unwrap();
assert_eq!(left.get(&id), Some(&Room::new(&id, &user)));
}
#[tokio::test]
async fn test_store_load_invited_room_state() {
let dir = tempdir().unwrap();
let path: &Path = dir.path();
let store = JsonStore::open(path).unwrap();
let id = RoomId::try_from("!roomid:example.com").unwrap();
let user = UserId::try_from("@example:example.com").unwrap();
let room = Room::new(&id, &user);
store
.store_room_state(RoomState::Invited(&room))
.await
.unwrap();
let AllRooms { invited, .. } = store.load_all_rooms().await.unwrap();
assert_eq!(invited.get(&id), Some(&Room::new(&id, &user)));
} }
#[tokio::test] #[tokio::test]