From 5b17e57c4ea65a8ac260dc19389e4fbc05b2fc90 Mon Sep 17 00:00:00 2001 From: Devin R Date: Wed, 29 Apr 2020 07:58:31 -0400 Subject: [PATCH 1/7] room/messages: add message de/ser to Room --- matrix_sdk/src/models/message.rs | 233 +++++++++++++++++++++++++++ matrix_sdk/src/models/mod.rs | 1 + matrix_sdk/src/models/room.rs | 52 ++++-- matrix_sdk/src/models/room_member.rs | 1 + matrix_sdk/src/state/mod.rs | 1 + 5 files changed, 274 insertions(+), 14 deletions(-) create mode 100644 matrix_sdk/src/models/message.rs diff --git a/matrix_sdk/src/models/message.rs b/matrix_sdk/src/models/message.rs new file mode 100644 index 00000000..6cb2482f --- /dev/null +++ b/matrix_sdk/src/models/message.rs @@ -0,0 +1,233 @@ +use std::collections::{vec_deque::IntoIter, VecDeque}; + +use crate::events::collections::all::RoomEvent; +use crate::events::room::message::MessageEvent; +use crate::events::EventJson; + +use serde::{de, ser}; + +pub(crate) mod ser_deser { + use super::*; + + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: de::Deserializer<'de>, + { + let messages: VecDeque> = de::Deserialize::deserialize(deserializer)?; + + // TODO this should probably bail out if deserialization fails not skip + let msgs: VecDeque = messages + .into_iter() + .flat_map(|json| json.deserialize()) + .collect(); + + Ok(MessageQueue { msgs }) + } + + pub fn serialize(msgs: &MessageQueue, serializer: S) -> Result + where + S: ser::Serializer, + { + use ser::Serialize; + + msgs.msgs.serialize(serializer) + } +} + +/// A queue that holds at most 10 messages received from the server. +#[derive(Clone, Debug, Default)] +pub struct MessageQueue { + msgs: VecDeque, +} + +impl PartialEq for MessageQueue { + fn eq(&self, other: &MessageQueue) -> bool { + self.msgs.len() == other.msgs.len() + && self + .msgs + .iter() + .zip(other.msgs.iter()) + .all(|(a, b)| match (a, b) { + (RoomEvent::RoomMessage(msg_a), RoomEvent::RoomMessage(msg_b)) => { + msg_a.event_id == msg_b.event_id + } + _ => false, + }) + } +} + +impl MessageQueue { + /// Create a new empty `MessageQueue`. + pub fn new() -> Self { + Self { + msgs: VecDeque::with_capacity(20), + } + } + + /// Appends a `MessageEvent` to the end of the `MessageQueue`. + /// + /// Removes the oldest element in the queue if there are more than 10 elements. + pub fn push(&mut self, msg: MessageEvent) -> bool { + self.msgs.push_back(RoomEvent::RoomMessage(msg)); + if self.msgs.len() > 10 { + self.msgs.pop_front(); + } + true + } + + pub fn iter(&self) -> impl Iterator { + self.msgs.iter() + } +} + +impl IntoIterator for MessageQueue { + type Item = RoomEvent; + type IntoIter = IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.msgs.into_iter() + } +} + +#[cfg(test)] +mod test { + use super::*; + + use std::collections::HashMap; + use std::convert::TryFrom; + + use crate::events::{collections::all::RoomEvent, EventJson}; + use crate::identifiers::{RoomId, UserId}; + use crate::{state::ClientState, Room}; + + #[test] + fn serialize() { + let id = RoomId::try_from("!roomid:example.com").unwrap(); + let user = UserId::try_from("@example:example.com").unwrap(); + + let mut room = Room::new(&id, &user); + + let json = std::fs::read_to_string("../test_data/events/message_text.json").unwrap(); + let event = serde_json::from_str::>(&json).unwrap(); + + let mut msgs = MessageQueue::new(); + if let Ok(ev) = event.deserialize() { + if let RoomEvent::RoomMessage(msg) = ev { + msgs.push(msg); + } + } + room.messages = msgs; + + let mut joined_rooms = HashMap::new(); + joined_rooms.insert(id, room); + + assert_eq!( + r#"{ + "!roomid:example.com": { + "room_id": "!roomid:example.com", + "room_name": { + "name": null, + "canonical_alias": null, + "aliases": [], + "heroes": [], + "joined_member_count": null, + "invited_member_count": null + }, + "own_user_id": "@example:example.com", + "creator": null, + "members": {}, + "messages": [ + { + "type": "m.room.message", + "content": { + "body": "is dancing", + "format": "org.matrix.custom.html", + "formatted_body": "is dancing", + "msgtype": "m.text" + }, + "event_id": "$152037280074GZeOm:localhost", + "origin_server_ts": 1520372800469, + "sender": "@example:localhost", + "unsigned": { + "age": 598971425 + } + } + ], + "typing_users": [], + "power_levels": null, + "encrypted": false, + "unread_highlight": null, + "unread_notifications": null, + "tombstone": null + } +}"#, + serde_json::to_string_pretty(&joined_rooms).unwrap() + ); + } + + #[test] + fn deserialize() { + let id = RoomId::try_from("!roomid:example.com").unwrap(); + let user = UserId::try_from("@example:example.com").unwrap(); + + let mut room = Room::new(&id, &user); + + let json = std::fs::read_to_string("../test_data/events/message_text.json").unwrap(); + let event = serde_json::from_str::>(&json).unwrap(); + + let mut msgs = MessageQueue::new(); + if let Ok(ev) = event.deserialize() { + if let RoomEvent::RoomMessage(msg) = ev { + msgs.push(msg); + } + } + room.messages = msgs; + + let mut joined_rooms = HashMap::new(); + joined_rooms.insert(id, room.clone()); + + let json = r#"{ + "!roomid:example.com": { + "room_id": "!roomid:example.com", + "room_name": { + "name": null, + "canonical_alias": null, + "aliases": [], + "heroes": [], + "joined_member_count": null, + "invited_member_count": null + }, + "own_user_id": "@example:example.com", + "creator": null, + "members": {}, + "messages": [ + { + "type": "m.room.message", + "content": { + "body": "is dancing", + "format": "org.matrix.custom.html", + "formatted_body": "is dancing", + "msgtype": "m.text" + }, + "event_id": "$152037280074GZeOm:localhost", + "origin_server_ts": 1520372800469, + "sender": "@example:localhost", + "unsigned": { + "age": 598971425 + } + } + ], + "typing_users": [], + "power_levels": null, + "encrypted": false, + "unread_highlight": null, + "unread_notifications": null, + "tombstone": null + } +}"#; + assert_eq!( + joined_rooms, + serde_json::from_str::>(json).unwrap() + ); + } +} diff --git a/matrix_sdk/src/models/mod.rs b/matrix_sdk/src/models/mod.rs index 5e461a32..41f4f461 100644 --- a/matrix_sdk/src/models/mod.rs +++ b/matrix_sdk/src/models/mod.rs @@ -1,4 +1,5 @@ mod event_deser; +mod message; mod room; mod room_member; diff --git a/matrix_sdk/src/models/room.rs b/matrix_sdk/src/models/room.rs index 92c808a7..f73d9409 100644 --- a/matrix_sdk/src/models/room.rs +++ b/matrix_sdk/src/models/room.rs @@ -16,6 +16,7 @@ use std::collections::{BTreeMap, HashMap}; use std::convert::TryFrom; +use super::message::MessageQueue; use super::RoomMember; use crate::api::r0::sync::sync_events::RoomSummary; @@ -26,6 +27,7 @@ use crate::events::room::{ canonical_alias::CanonicalAliasEvent, encryption::EncryptionEvent, member::{MemberEvent, MembershipChange}, + message::MessageEvent, name::NameEvent, power_levels::{NotificationPowerLevels, PowerLevelsEvent, PowerLevelsEventContent}, tombstone::TombstoneEvent, @@ -36,6 +38,7 @@ use crate::identifiers::{RoomAliasId, RoomId, UserId}; use crate::js_int::{Int, UInt}; use serde::{Deserialize, Serialize}; #[derive(Debug, Default, PartialEq, Serialize, Deserialize)] +#[cfg_attr(test, derive(Clone))] /// `RoomName` allows the calculation of a text room name. pub struct RoomName { /// The displayed name of the room. @@ -58,6 +61,7 @@ pub struct RoomName { } #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(test, derive(Clone))] pub struct PowerLevels { /// The level required to ban a user. pub ban: Int, @@ -84,6 +88,7 @@ pub struct PowerLevels { } #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(test, derive(Clone))] pub struct Tombstone { /// A server-defined message. body: String, @@ -92,6 +97,7 @@ pub struct Tombstone { } #[derive(Debug, PartialEq, Serialize, Deserialize)] +#[cfg_attr(test, derive(Clone))] /// A Matrix room. pub struct Room { /// The unique id of the room. @@ -104,6 +110,12 @@ pub struct Room { pub creator: Option, /// The map of room members. pub members: HashMap, + /// A queue of messages no longer than MAX_MSGS + /// + /// This is helpful when using a `StateStore` to avoid multiple requests + /// to the server for messages. + #[serde(with = "super::message::ser_deser")] + pub messages: MessageQueue, /// A list of users that are currently typing. pub typing_users: Vec, /// The power level requirements for specific actions in this room @@ -207,6 +219,7 @@ impl Room { own_user_id: own_user_id.clone(), creator: None, members: HashMap::new(), + messages: MessageQueue::new(), typing_users: Vec::new(), power_levels: None, encrypted: false, @@ -320,6 +333,13 @@ impl Room { } } + /// Handle a room.message event and update the `MessageQueue` if necessary. + /// + /// Returns true if `MessageQueue` was added to. + pub fn handle_message(&mut self, event: &MessageEvent) -> bool { + self.messages.push(event.clone()) + } + /// Handle a room.aliases event, updating the room state if necessary. /// /// Returns true if the room name changed, false otherwise. @@ -396,15 +416,16 @@ impl Room { pub fn receive_timeline_event(&mut self, event: &RoomEvent) -> bool { match event { // update to the current members of the room - RoomEvent::RoomMember(m) => self.handle_membership(m), + RoomEvent::RoomMember(member) => self.handle_membership(member), // finds all events related to the name of the room for later use - RoomEvent::RoomName(n) => self.handle_room_name(n), - RoomEvent::RoomCanonicalAlias(ca) => self.handle_canonical(ca), - RoomEvent::RoomAliases(a) => self.handle_room_aliases(a), + RoomEvent::RoomName(name) => self.handle_room_name(name), + RoomEvent::RoomCanonicalAlias(c_alias) => self.handle_canonical(c_alias), + RoomEvent::RoomAliases(alias) => self.handle_room_aliases(alias), // power levels of the room members - RoomEvent::RoomPowerLevels(p) => self.handle_power_level(p), - RoomEvent::RoomTombstone(t) => self.handle_tombstone(t), - RoomEvent::RoomEncryption(e) => self.handle_encryption_event(e), + RoomEvent::RoomPowerLevels(power) => self.handle_power_level(power), + RoomEvent::RoomTombstone(tomb) => self.handle_tombstone(tomb), + RoomEvent::RoomEncryption(encrypt) => self.handle_encryption_event(encrypt), + RoomEvent::RoomMessage(msg) => self.handle_message(msg), _ => false, } } @@ -418,13 +439,16 @@ impl Room { /// * `event` - The event of the room. pub fn receive_state_event(&mut self, event: &StateEvent) -> bool { match event { - StateEvent::RoomMember(m) => self.handle_membership(m), - StateEvent::RoomName(n) => self.handle_room_name(n), - StateEvent::RoomCanonicalAlias(ca) => self.handle_canonical(ca), - StateEvent::RoomAliases(a) => self.handle_room_aliases(a), - StateEvent::RoomPowerLevels(p) => self.handle_power_level(p), - StateEvent::RoomTombstone(t) => self.handle_tombstone(t), - StateEvent::RoomEncryption(e) => self.handle_encryption_event(e), + // update to the current members of the room + StateEvent::RoomMember(member) => self.handle_membership(member), + // finds all events related to the name of the room for later use + StateEvent::RoomName(name) => self.handle_room_name(name), + StateEvent::RoomCanonicalAlias(c_alias) => self.handle_canonical(c_alias), + StateEvent::RoomAliases(alias) => self.handle_room_aliases(alias), + // power levels of the room members + StateEvent::RoomPowerLevels(power) => self.handle_power_level(power), + StateEvent::RoomTombstone(tomb) => self.handle_tombstone(tomb), + StateEvent::RoomEncryption(encrypt) => self.handle_encryption_event(encrypt), _ => false, } } diff --git a/matrix_sdk/src/models/room_member.rs b/matrix_sdk/src/models/room_member.rs index 882d8751..e0270b00 100644 --- a/matrix_sdk/src/models/room_member.rs +++ b/matrix_sdk/src/models/room_member.rs @@ -28,6 +28,7 @@ use serde::{Deserialize, Serialize}; // Notes: if Alice invites Bob into a room we will get an event with the sender as Alice and the state key as Bob. #[derive(Debug, Serialize, Deserialize)] +#[cfg_attr(test, derive(Clone))] /// A Matrix room member. /// pub struct RoomMember { diff --git a/matrix_sdk/src/state/mod.rs b/matrix_sdk/src/state/mod.rs index 3ca6ea7e..78a35807 100644 --- a/matrix_sdk/src/state/mod.rs +++ b/matrix_sdk/src/state/mod.rs @@ -125,6 +125,7 @@ mod test { "own_user_id": "@example:example.com", "creator": null, "members": {}, + "messages": [], "typing_users": [], "power_levels": null, "encrypted": false, From 9386b500a8ff70f401efc51319d5ab8ce852e970 Mon Sep 17 00:00:00 2001 From: Devin R Date: Wed, 29 Apr 2020 19:28:58 -0400 Subject: [PATCH 2/7] message: keep MessageQueue sorted by origin_server_ts --- matrix_sdk/src/models/message.rs | 130 ++++++++++++++++++++----------- 1 file changed, 86 insertions(+), 44 deletions(-) diff --git a/matrix_sdk/src/models/message.rs b/matrix_sdk/src/models/message.rs index 6cb2482f..f867a4cc 100644 --- a/matrix_sdk/src/models/message.rs +++ b/matrix_sdk/src/models/message.rs @@ -1,43 +1,52 @@ -use std::collections::{vec_deque::IntoIter, VecDeque}; +use std::cmp::Ordering; +use std::ops::Deref; +use std::vec::IntoIter; use crate::events::collections::all::RoomEvent; use crate::events::room::message::MessageEvent; use crate::events::EventJson; -use serde::{de, ser}; - -pub(crate) mod ser_deser { - use super::*; - - pub fn deserialize<'de, D>(deserializer: D) -> Result - where - D: de::Deserializer<'de>, - { - let messages: VecDeque> = de::Deserialize::deserialize(deserializer)?; - - // TODO this should probably bail out if deserialization fails not skip - let msgs: VecDeque = messages - .into_iter() - .flat_map(|json| json.deserialize()) - .collect(); - - Ok(MessageQueue { msgs }) - } - - pub fn serialize(msgs: &MessageQueue, serializer: S) -> Result - where - S: ser::Serializer, - { - use ser::Serialize; - - msgs.msgs.serialize(serializer) - } -} +use serde::{de, ser, Serialize}; /// A queue that holds at most 10 messages received from the server. #[derive(Clone, Debug, Default)] pub struct MessageQueue { - msgs: VecDeque, + msgs: Vec, +} + +#[derive(Clone, Debug, Serialize)] +pub struct MessageWrapper(MessageEvent); + +impl Deref for MessageWrapper { + type Target = MessageEvent; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl PartialEq for MessageWrapper { + fn eq(&self, other: &MessageWrapper) -> bool { + self.0.event_id == other.0.event_id + && self.0.room_id == other.0.room_id + && self.0.origin_server_ts == other.0.origin_server_ts + && self.0.sender == other.0.sender + && self.0.content == other.0.content + } +} + +impl Eq for MessageWrapper {} + +impl PartialOrd for MessageWrapper { + fn partial_cmp(&self, other: &MessageWrapper) -> Option { + Some(self.0.origin_server_ts.cmp(&other.0.origin_server_ts)) + } +} + +impl Ord for MessageWrapper { + fn cmp(&self, other: &MessageWrapper) -> Ordering { + self.partial_cmp(other).unwrap_or(Ordering::Equal) + } } impl PartialEq for MessageQueue { @@ -47,12 +56,7 @@ impl PartialEq for MessageQueue { .msgs .iter() .zip(other.msgs.iter()) - .all(|(a, b)| match (a, b) { - (RoomEvent::RoomMessage(msg_a), RoomEvent::RoomMessage(msg_b)) => { - msg_a.event_id == msg_b.event_id - } - _ => false, - }) + .all(|(msg_a, msg_b)| msg_a.event_id == msg_b.event_id) } } @@ -60,28 +64,32 @@ impl MessageQueue { /// Create a new empty `MessageQueue`. pub fn new() -> Self { Self { - msgs: VecDeque::with_capacity(20), + msgs: Vec::with_capacity(20), } } - /// Appends a `MessageEvent` to the end of the `MessageQueue`. + /// Inserts a `MessageEvent` into `MessageQueue`, sorted by by `origin_server_ts`. /// /// Removes the oldest element in the queue if there are more than 10 elements. pub fn push(&mut self, msg: MessageEvent) -> bool { - self.msgs.push_back(RoomEvent::RoomMessage(msg)); + let message = MessageWrapper(msg); + match self.msgs.binary_search_by(|m| m.cmp(&message)) { + Ok(pos) => self.msgs.insert(pos, message), + Err(pos) => self.msgs.insert(pos, message), + } if self.msgs.len() > 10 { - self.msgs.pop_front(); + self.msgs.remove(0); } true } - pub fn iter(&self) -> impl Iterator { + pub fn iter(&self) -> impl Iterator { self.msgs.iter() } } impl IntoIterator for MessageQueue { - type Item = RoomEvent; + type Item = MessageWrapper; type IntoIter = IntoIter; fn into_iter(self) -> Self::IntoIter { @@ -89,6 +97,40 @@ impl IntoIterator for MessageQueue { } } +pub(crate) mod ser_deser { + use super::*; + + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: de::Deserializer<'de>, + { + let messages: Vec> = de::Deserialize::deserialize(deserializer)?; + + // TODO this should probably bail out if deserialization fails not skip + let msgs: Vec = messages + .into_iter() + .flat_map(|json| json.deserialize()) + .flat_map(|ev| { + if let RoomEvent::RoomMessage(msg) = ev { + Some(msg) + } else { + None + } + }) + .map(MessageWrapper) + .collect(); + + Ok(MessageQueue { msgs }) + } + + pub fn serialize(msgs: &MessageQueue, serializer: S) -> Result + where + S: ser::Serializer, + { + msgs.msgs.serialize(serializer) + } +} + #[cfg(test)] mod test { use super::*; @@ -98,7 +140,7 @@ mod test { use crate::events::{collections::all::RoomEvent, EventJson}; use crate::identifiers::{RoomId, UserId}; - use crate::{state::ClientState, Room}; + use crate::Room; #[test] fn serialize() { From 9788233771e015533bbfc29855bd122a520ce633 Mon Sep 17 00:00:00 2001 From: Devin R Date: Thu, 30 Apr 2020 06:28:36 -0400 Subject: [PATCH 3/7] room/message: make docs more clear, deserialize to MessageEvent over RoomEvent --- matrix_sdk/src/models/message.rs | 14 +++----------- matrix_sdk/src/models/room.rs | 2 +- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/matrix_sdk/src/models/message.rs b/matrix_sdk/src/models/message.rs index f867a4cc..e65de74a 100644 --- a/matrix_sdk/src/models/message.rs +++ b/matrix_sdk/src/models/message.rs @@ -2,13 +2,12 @@ use std::cmp::Ordering; use std::ops::Deref; use std::vec::IntoIter; -use crate::events::collections::all::RoomEvent; use crate::events::room::message::MessageEvent; use crate::events::EventJson; use serde::{de, ser, Serialize}; -/// A queue that holds at most 10 messages received from the server. +/// A queue that holds the 10 most recent messages received from the server. #[derive(Clone, Debug, Default)] pub struct MessageQueue { msgs: Vec, @@ -104,19 +103,12 @@ pub(crate) mod ser_deser { where D: de::Deserializer<'de>, { - let messages: Vec> = de::Deserialize::deserialize(deserializer)?; + let messages: Vec> = de::Deserialize::deserialize(deserializer)?; - // TODO this should probably bail out if deserialization fails not skip + // TODO this should probably bail out if deserialization fails not skip the message let msgs: Vec = messages .into_iter() .flat_map(|json| json.deserialize()) - .flat_map(|ev| { - if let RoomEvent::RoomMessage(msg) = ev { - Some(msg) - } else { - None - } - }) .map(MessageWrapper) .collect(); diff --git a/matrix_sdk/src/models/room.rs b/matrix_sdk/src/models/room.rs index f73d9409..40e9924b 100644 --- a/matrix_sdk/src/models/room.rs +++ b/matrix_sdk/src/models/room.rs @@ -110,7 +110,7 @@ pub struct Room { pub creator: Option, /// The map of room members. pub members: HashMap, - /// A queue of messages no longer than MAX_MSGS + /// A queue of messages, holds no more than 10 of the most recent messages. /// /// This is helpful when using a `StateStore` to avoid multiple requests /// to the server for messages. From 2c4b6919ef42623b8275d8bfeba183b8e352e4c8 Mon Sep 17 00:00:00 2001 From: Devin R Date: Thu, 30 Apr 2020 06:57:25 -0400 Subject: [PATCH 4/7] message: only push message when timestamp is larger than last msg in queue --- matrix_sdk/src/models/message.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/matrix_sdk/src/models/message.rs b/matrix_sdk/src/models/message.rs index e65de74a..9ad81820 100644 --- a/matrix_sdk/src/models/message.rs +++ b/matrix_sdk/src/models/message.rs @@ -71,6 +71,13 @@ impl MessageQueue { /// /// Removes the oldest element in the queue if there are more than 10 elements. pub fn push(&mut self, msg: MessageEvent) -> bool { + // only push new messages into the queue + if let Some(latest) = self.msgs.last() { + if msg.origin_server_ts < latest.origin_server_ts { + return false; + } + } + let message = MessageWrapper(msg); match self.msgs.binary_search_by(|m| m.cmp(&message)) { Ok(pos) => self.msgs.insert(pos, message), From bd2d6b0fac37dbfd3db531918eac14e28123fee0 Mon Sep 17 00:00:00 2001 From: Devin R Date: Tue, 5 May 2020 07:04:39 -0400 Subject: [PATCH 5/7] message: push message when queue isn't full, don't insert dup --- matrix_sdk/src/models/message.rs | 62 ++++++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/matrix_sdk/src/models/message.rs b/matrix_sdk/src/models/message.rs index 9ad81820..ec531b8c 100644 --- a/matrix_sdk/src/models/message.rs +++ b/matrix_sdk/src/models/message.rs @@ -30,7 +30,6 @@ impl PartialEq for MessageWrapper { && self.0.room_id == other.0.room_id && self.0.origin_server_ts == other.0.origin_server_ts && self.0.sender == other.0.sender - && self.0.content == other.0.content } } @@ -73,14 +72,18 @@ impl MessageQueue { pub fn push(&mut self, msg: MessageEvent) -> bool { // only push new messages into the queue if let Some(latest) = self.msgs.last() { - if msg.origin_server_ts < latest.origin_server_ts { + if msg.origin_server_ts < latest.origin_server_ts && self.msgs.len() >= 10 { return false; } } let message = MessageWrapper(msg); match self.msgs.binary_search_by(|m| m.cmp(&message)) { - Ok(pos) => self.msgs.insert(pos, message), + Ok(pos) => { + if self.msgs[pos] != message { + self.msgs.insert(pos, message) + } + } Err(pos) => self.msgs.insert(pos, message), } if self.msgs.len() > 10 { @@ -161,7 +164,52 @@ mod test { let mut joined_rooms = HashMap::new(); joined_rooms.insert(id, room); - + println!("{}", serde_json::to_string_pretty(&joined_rooms).unwrap()); + // this is the correct JSON string changes to `ruma-events` have not been released + // that would fix the doubling of fields + // TODO uncomment when fixed + // assert_eq!( + // r#"{ + // "!roomid:example.com": { + // "room_id": "!roomid:example.com", + // "room_name": { + // "name": null, + // "canonical_alias": null, + // "aliases": [], + // "heroes": [], + // "joined_member_count": null, + // "invited_member_count": null + // }, + // "own_user_id": "@example:example.com", + // "creator": null, + // "members": {}, + // "messages": [ + // { + // "type": "m.room.message", + // "content": { + // "body": "is dancing", + // "format": "org.matrix.custom.html", + // "formatted_body": "is dancing", + // "msgtype": "m.text" + // }, + // "event_id": "$152037280074GZeOm:localhost", + // "origin_server_ts": 1520372800469, + // "sender": "@example:localhost", + // "unsigned": { + // "age": 598971425 + // } + // } + // ], + // "typing_users": [], + // "power_levels": null, + // "encrypted": false, + // "unread_highlight": null, + // "unread_notifications": null, + // "tombstone": null + // } + // }"#, + // serde_json::to_string_pretty(&joined_rooms).unwrap() + // ); assert_eq!( r#"{ "!roomid:example.com": { @@ -179,12 +227,12 @@ mod test { "members": {}, "messages": [ { - "type": "m.room.message", "content": { + "msgtype": "m.text", + "msgtype": "m.text", "body": "is dancing", "format": "org.matrix.custom.html", - "formatted_body": "is dancing", - "msgtype": "m.text" + "formatted_body": "is dancing" }, "event_id": "$152037280074GZeOm:localhost", "origin_server_ts": 1520372800469, From c90eb6a9386a12e132b30f2769c243f426c75cbc Mon Sep 17 00:00:00 2001 From: Devin R Date: Wed, 6 May 2020 06:58:42 -0400 Subject: [PATCH 6/7] message: resolve reviews, deserialization can fail, update for ruma removed PartialEq --- matrix_sdk/src/models/message.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/matrix_sdk/src/models/message.rs b/matrix_sdk/src/models/message.rs index ec531b8c..d1526662 100644 --- a/matrix_sdk/src/models/message.rs +++ b/matrix_sdk/src/models/message.rs @@ -27,9 +27,6 @@ impl Deref for MessageWrapper { impl PartialEq for MessageWrapper { fn eq(&self, other: &MessageWrapper) -> bool { self.0.event_id == other.0.event_id - && self.0.room_id == other.0.room_id - && self.0.origin_server_ts == other.0.origin_server_ts - && self.0.sender == other.0.sender } } @@ -113,14 +110,15 @@ pub(crate) mod ser_deser { where D: de::Deserializer<'de>, { + use serde::de::Error; + let messages: Vec> = de::Deserialize::deserialize(deserializer)?; - // TODO this should probably bail out if deserialization fails not skip the message - let msgs: Vec = messages - .into_iter() - .flat_map(|json| json.deserialize()) - .map(MessageWrapper) - .collect(); + let mut msgs = vec![]; + for json in messages { + let msg = json.deserialize().map_err(D::Error::custom)?; + msgs.push(MessageWrapper(msg)); + } Ok(MessageQueue { msgs }) } From 9939efe0e84b16b44ee46ae8e3867c22f88fff06 Mon Sep 17 00:00:00 2001 From: Devin R Date: Wed, 6 May 2020 21:17:21 -0400 Subject: [PATCH 7/7] message: feature flag message queue storage in Room struct --- matrix_sdk/Cargo.toml | 1 + matrix_sdk/src/models/message.rs | 5 +++++ matrix_sdk/src/models/mod.rs | 2 ++ matrix_sdk/src/models/room.rs | 12 +++++++++++- matrix_sdk/src/state/mod.rs | 29 +++++++++++++++++++++++++++++ 5 files changed, 48 insertions(+), 1 deletion(-) diff --git a/matrix_sdk/Cargo.toml b/matrix_sdk/Cargo.toml index a21cde7c..92ea298b 100644 --- a/matrix_sdk/Cargo.toml +++ b/matrix_sdk/Cargo.toml @@ -12,6 +12,7 @@ version = "0.1.0" [features] default = ["encryption", "sqlite-cryptostore"] +messages = [] encryption = ["matrix-sdk-crypto"] sqlite-cryptostore = ["matrix-sdk-crypto/sqlite-cryptostore"] diff --git a/matrix_sdk/src/models/message.rs b/matrix_sdk/src/models/message.rs index d1526662..2aed0ec5 100644 --- a/matrix_sdk/src/models/message.rs +++ b/matrix_sdk/src/models/message.rs @@ -1,3 +1,8 @@ +//! A queue that holds at most ten of the most recent messages. +//! +//! The `Room` struct optionally holds a `MessageQueue` if the "messages" +//! feature is enabled. + use std::cmp::Ordering; use std::ops::Deref; use std::vec::IntoIter; diff --git a/matrix_sdk/src/models/mod.rs b/matrix_sdk/src/models/mod.rs index 41f4f461..6604d61d 100644 --- a/matrix_sdk/src/models/mod.rs +++ b/matrix_sdk/src/models/mod.rs @@ -1,4 +1,6 @@ mod event_deser; +#[cfg(feature = "messages")] +#[cfg_attr(docsrs, doc(cfg(feature = "messages")))] mod message; mod room; mod room_member; diff --git a/matrix_sdk/src/models/room.rs b/matrix_sdk/src/models/room.rs index 40e9924b..197030bf 100644 --- a/matrix_sdk/src/models/room.rs +++ b/matrix_sdk/src/models/room.rs @@ -16,6 +16,7 @@ use std::collections::{BTreeMap, HashMap}; use std::convert::TryFrom; +#[cfg(feature = "messages")] use super::message::MessageQueue; use super::RoomMember; @@ -27,12 +28,15 @@ use crate::events::room::{ canonical_alias::CanonicalAliasEvent, encryption::EncryptionEvent, member::{MemberEvent, MembershipChange}, - message::MessageEvent, name::NameEvent, power_levels::{NotificationPowerLevels, PowerLevelsEvent, PowerLevelsEventContent}, tombstone::TombstoneEvent, }; use crate::events::EventType; + +#[cfg(feature = "messages")] +use crate::events::room::message::MessageEvent; + use crate::identifiers::{RoomAliasId, RoomId, UserId}; use crate::js_int::{Int, UInt}; @@ -114,6 +118,8 @@ pub struct Room { /// /// This is helpful when using a `StateStore` to avoid multiple requests /// to the server for messages. + #[cfg(feature = "messages")] + #[cfg_attr(docsrs, doc(cfg(feature = "messages")))] #[serde(with = "super::message::ser_deser")] pub messages: MessageQueue, /// A list of users that are currently typing. @@ -219,6 +225,7 @@ impl Room { own_user_id: own_user_id.clone(), creator: None, members: HashMap::new(), + #[cfg(feature = "messages")] messages: MessageQueue::new(), typing_users: Vec::new(), power_levels: None, @@ -336,6 +343,8 @@ impl Room { /// Handle a room.message event and update the `MessageQueue` if necessary. /// /// Returns true if `MessageQueue` was added to. + #[cfg(feature = "messages")] + #[cfg_attr(docsrs, doc(cfg(feature = "messages")))] pub fn handle_message(&mut self, event: &MessageEvent) -> bool { self.messages.push(event.clone()) } @@ -425,6 +434,7 @@ impl Room { RoomEvent::RoomPowerLevels(power) => self.handle_power_level(power), RoomEvent::RoomTombstone(tomb) => self.handle_tombstone(tomb), RoomEvent::RoomEncryption(encrypt) => self.handle_encryption_event(encrypt), + #[cfg(feature = "messages")] RoomEvent::RoomMessage(msg) => self.handle_message(msg), _ => false, } diff --git a/matrix_sdk/src/state/mod.rs b/matrix_sdk/src/state/mod.rs index 04b45f94..c4befd4d 100644 --- a/matrix_sdk/src/state/mod.rs +++ b/matrix_sdk/src/state/mod.rs @@ -107,6 +107,35 @@ mod test { let mut joined_rooms = HashMap::new(); joined_rooms.insert(id, room); + + #[cfg(not(feature = "messages"))] + assert_eq!( + r#"{ + "!roomid:example.com": { + "room_id": "!roomid:example.com", + "room_name": { + "name": null, + "canonical_alias": null, + "aliases": [], + "heroes": [], + "joined_member_count": null, + "invited_member_count": null + }, + "own_user_id": "@example:example.com", + "creator": null, + "members": {}, + "typing_users": [], + "power_levels": null, + "encrypted": false, + "unread_highlight": null, + "unread_notifications": null, + "tombstone": null + } +}"#, + serde_json::to_string_pretty(&joined_rooms).unwrap() + ); + + #[cfg(feature = "messages")] assert_eq!( r#"{ "!roomid:example.com": {