|
|
|
@ -1,7 +1,8 @@ |
|
|
|
use std::collections::{HashMap, HashSet};
|
|
|
|
use std::mem;
|
|
|
|
use std::time::SystemTime;
|
|
|
|
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
|
|
|
use solstice_proto::{server, User};
|
|
|
|
use thiserror::Error;
|
|
|
|
|
|
|
|
@ -32,13 +33,78 @@ pub enum Visibility { |
|
|
|
PrivateOther,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// This structure contains a chat room message.
|
|
|
|
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
|
|
|
/// A message sent to a chat room.
|
|
|
|
#[derive(
|
|
|
|
Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize,
|
|
|
|
)]
|
|
|
|
pub struct Message {
|
|
|
|
/// Time at which the message was received by this client.
|
|
|
|
///
|
|
|
|
/// We use `SystemTime` instead of `Instant` because this is serialized and
|
|
|
|
/// sent to API clients.
|
|
|
|
///
|
|
|
|
/// Defined first in order for the `Ord` and `PartialOrd` derived traits to
|
|
|
|
/// sort by this key first.
|
|
|
|
pub received_at: SystemTime,
|
|
|
|
|
|
|
|
/// The user name of the message sender.
|
|
|
|
pub user_name: String,
|
|
|
|
|
|
|
|
/// The contents of the message.
|
|
|
|
pub message: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The history of messages sent for a single chat room.
|
|
|
|
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
|
|
|
pub struct MessageHistory {
|
|
|
|
/// Messages, sorted in increasing order.
|
|
|
|
messages: Vec<Message>,
|
|
|
|
}
|
|
|
|
|
|
|
|
// MessageHistory should be transparent for serialization purposes.
|
|
|
|
impl<'de> Deserialize<'de> for MessageHistory {
|
|
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
|
|
where
|
|
|
|
D: Deserializer<'de>,
|
|
|
|
{
|
|
|
|
let messages = Vec::<Message>::deserialize(deserializer)?;
|
|
|
|
Ok(Self::new(messages))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// MessageHistory should be transparent for serialization purposes.
|
|
|
|
impl Serialize for MessageHistory {
|
|
|
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
|
|
where
|
|
|
|
S: Serializer,
|
|
|
|
{
|
|
|
|
self.messages.serialize(serializer)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl MessageHistory {
|
|
|
|
pub fn new(mut messages: Vec<Message>) -> Self {
|
|
|
|
messages.sort();
|
|
|
|
Self { messages }
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Inserts a `message` into this history.
|
|
|
|
pub fn insert(&mut self, message: Message) {
|
|
|
|
self.messages.push(message);
|
|
|
|
|
|
|
|
// This could be terrible for performance in the general case, but we know
|
|
|
|
// that messages should be coming in almost-always sorted since
|
|
|
|
// `received_at` is usually set to `now()`.
|
|
|
|
self.messages.sort();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
/// Returns the list of messages sorted in increasing chronological order.
|
|
|
|
pub fn to_vec(&self) -> Vec<Message> {
|
|
|
|
return self.messages.clone();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// This structure contains the last known information about a chat room.
|
|
|
|
/// It does not store the name, as that is stored implicitly as the key in the
|
|
|
|
/// room hash table.
|
|
|
|
@ -59,8 +125,8 @@ pub struct Room { |
|
|
|
pub operators: HashSet<String>,
|
|
|
|
/// The names of the room's members.
|
|
|
|
pub members: HashSet<String>,
|
|
|
|
/// The messages sent to this chat room, in chronological order.
|
|
|
|
pub messages: Vec<Message>,
|
|
|
|
/// The messages sent to this chat room.
|
|
|
|
pub messages: MessageHistory,
|
|
|
|
/// The tickers displayed in this room.
|
|
|
|
pub tickers: Vec<(String, String)>,
|
|
|
|
}
|
|
|
|
@ -76,7 +142,7 @@ impl Room { |
|
|
|
owner: None,
|
|
|
|
operators: HashSet::new(),
|
|
|
|
members: HashSet::new(),
|
|
|
|
messages: Vec::new(),
|
|
|
|
messages: MessageHistory::default(),
|
|
|
|
tickers: Vec::new(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -290,17 +356,6 @@ impl RoomMap { |
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Saves the given message as the last one in the given room.
|
|
|
|
pub fn add_message(
|
|
|
|
&mut self,
|
|
|
|
room_name: &str,
|
|
|
|
message: Message,
|
|
|
|
) -> Result<(), RoomError> {
|
|
|
|
let room = self.get_mut_strict(room_name)?;
|
|
|
|
room.messages.push(message);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Inserts the given user in the given room's set of members.
|
|
|
|
/// Returns an error if the room is not found.
|
|
|
|
pub fn insert_member(
|
|
|
|
@ -342,9 +397,11 @@ impl RoomMap { |
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use std::time::{Duration, SystemTime};
|
|
|
|
|
|
|
|
use solstice_proto::server::RoomListResponse;
|
|
|
|
|
|
|
|
use super::{Membership, Message, Room, RoomMap, Visibility};
|
|
|
|
use super::{Membership, Message, MessageHistory, Room, RoomMap, Visibility};
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn deserialize_membership() {
|
|
|
|
@ -366,10 +423,17 @@ mod tests { |
|
|
|
fn deserialize_message() {
|
|
|
|
assert_eq!(
|
|
|
|
serde_json::from_str::<Message>(
|
|
|
|
r#"{ "user_name":"karandeep", "message":"namaste" }"#
|
|
|
|
r#"{
|
|
|
|
"received_at": { "secs_since_epoch": 42, "nanos_since_epoch": 1337 },
|
|
|
|
"user_name":"karandeep",
|
|
|
|
"message":"namaste"
|
|
|
|
}"#
|
|
|
|
)
|
|
|
|
.unwrap(),
|
|
|
|
Message {
|
|
|
|
received_at: SystemTime::UNIX_EPOCH
|
|
|
|
+ Duration::from_secs(42)
|
|
|
|
+ Duration::from_nanos(1337),
|
|
|
|
user_name: "karandeep".to_string(),
|
|
|
|
message: "namaste".to_string()
|
|
|
|
}
|
|
|
|
@ -389,8 +453,16 @@ mod tests { |
|
|
|
"operators": ["op1", "op2"],
|
|
|
|
"members": ["m1", "m2"],
|
|
|
|
"messages": [
|
|
|
|
{ "user_name": "u1", "message": "msg1" },
|
|
|
|
{ "user_name": "u2", "message": "msg2" }
|
|
|
|
{
|
|
|
|
"received_at": { "secs_since_epoch": 43, "nanos_since_epoch": 0 },
|
|
|
|
"user_name": "u2",
|
|
|
|
"message": "msg2"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"received_at": { "secs_since_epoch": 42, "nanos_since_epoch": 0 },
|
|
|
|
"user_name": "u1",
|
|
|
|
"message": "msg1"
|
|
|
|
}
|
|
|
|
],
|
|
|
|
"tickers": [["t11", "t12"], ["t21", "t22"]]
|
|
|
|
}"#
|
|
|
|
@ -410,16 +482,18 @@ mod tests { |
|
|
|
.iter()
|
|
|
|
.cloned()
|
|
|
|
.collect(),
|
|
|
|
messages: vec![
|
|
|
|
messages: MessageHistory::new(vec![
|
|
|
|
Message {
|
|
|
|
received_at: SystemTime::UNIX_EPOCH + Duration::from_secs(42),
|
|
|
|
user_name: "u1".to_string(),
|
|
|
|
message: "msg1".to_string(),
|
|
|
|
},
|
|
|
|
Message {
|
|
|
|
received_at: SystemTime::UNIX_EPOCH + Duration::from_secs(43),
|
|
|
|
user_name: "u2".to_string(),
|
|
|
|
message: "msg2".to_string(),
|
|
|
|
}
|
|
|
|
],
|
|
|
|
]),
|
|
|
|
tickers: vec![
|
|
|
|
("t11".to_string(), "t12".to_string()),
|
|
|
|
("t21".to_string(), "t22".to_string()),
|
|
|
|
|