|
|
@ -1,153 +1,11 @@ |
|
|
use std::collections::{HashMap, HashSet};
|
|
|
|
|
|
|
|
|
use std::collections::HashMap;
|
|
|
use std::mem;
|
|
|
use std::mem;
|
|
|
use std::time::SystemTime;
|
|
|
|
|
|
|
|
|
|
|
|
use log::{error, info, warn};
|
|
|
use log::{error, info, warn};
|
|
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
|
|
|
|
|
use solstice_proto::{server, User};
|
|
|
use solstice_proto::{server, User};
|
|
|
use thiserror::Error;
|
|
|
use thiserror::Error;
|
|
|
|
|
|
|
|
|
/// This enumeration is the list of possible membership states for a chat room.
|
|
|
|
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
|
|
|
|
|
pub enum Membership {
|
|
|
|
|
|
/// The user is not a member of this room.
|
|
|
|
|
|
NonMember,
|
|
|
|
|
|
/// The user has requested to join the room, but hasn't heard back from the
|
|
|
|
|
|
/// server yet.
|
|
|
|
|
|
Joining,
|
|
|
|
|
|
/// The user is a member of the room.
|
|
|
|
|
|
Member,
|
|
|
|
|
|
/// The user has request to leave the room, but hasn't heard back from the
|
|
|
|
|
|
/// server yet.
|
|
|
|
|
|
Leaving,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// This enumeration is the list of visibility types for rooms that the user is
|
|
|
|
|
|
/// a member of.
|
|
|
|
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
|
|
|
|
|
pub enum Visibility {
|
|
|
|
|
|
/// This room is visible to any user.
|
|
|
|
|
|
Public,
|
|
|
|
|
|
/// This room is visible only to members, and the user owns it.
|
|
|
|
|
|
PrivateOwned,
|
|
|
|
|
|
/// This room is visible only to members, and someone else owns it.
|
|
|
|
|
|
PrivateOther,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 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.
|
|
|
|
|
|
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
|
|
|
|
|
pub struct Room {
|
|
|
|
|
|
/// The membership state of the user for the room.
|
|
|
|
|
|
pub membership: Membership,
|
|
|
|
|
|
/// The visibility of the room.
|
|
|
|
|
|
pub visibility: Visibility,
|
|
|
|
|
|
/// True if the user is one of the room's operators, False if the user is a
|
|
|
|
|
|
/// regular member.
|
|
|
|
|
|
pub operated: bool,
|
|
|
|
|
|
/// The number of users that are members of the room.
|
|
|
|
|
|
pub user_count: usize,
|
|
|
|
|
|
/// The name of the room's owner, if any.
|
|
|
|
|
|
pub owner: Option<String>,
|
|
|
|
|
|
/// The names of the room's operators.
|
|
|
|
|
|
pub operators: HashSet<String>,
|
|
|
|
|
|
/// The names of the room's members.
|
|
|
|
|
|
pub members: HashSet<String>,
|
|
|
|
|
|
/// The messages sent to this chat room.
|
|
|
|
|
|
pub messages: MessageHistory,
|
|
|
|
|
|
/// The tickers displayed in this room.
|
|
|
|
|
|
pub tickers: Vec<(String, String)>,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl Room {
|
|
|
|
|
|
/// Creates a new room with the given visibility and user count.
|
|
|
|
|
|
pub fn new(visibility: Visibility, user_count: usize) -> Self {
|
|
|
|
|
|
Room {
|
|
|
|
|
|
membership: Membership::NonMember,
|
|
|
|
|
|
visibility: visibility,
|
|
|
|
|
|
operated: false,
|
|
|
|
|
|
user_count: user_count,
|
|
|
|
|
|
owner: None,
|
|
|
|
|
|
operators: HashSet::new(),
|
|
|
|
|
|
members: HashSet::new(),
|
|
|
|
|
|
messages: MessageHistory::default(),
|
|
|
|
|
|
tickers: Vec::new(),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
use crate::room::{Membership, Room, Visibility};
|
|
|
|
|
|
|
|
|
/// The error returned by RoomMap functions.
|
|
|
/// The error returned by RoomMap functions.
|
|
|
#[derive(Debug, Error)]
|
|
|
#[derive(Debug, Error)]
|
|
|
@ -175,7 +33,6 @@ impl RoomMap { |
|
|
|
|
|
|
|
|
/// Inserts the given room in the map under the given name.
|
|
|
/// Inserts the given room in the map under the given name.
|
|
|
/// Same semantics as `std::collections::HashMap::insert()`.
|
|
|
/// Same semantics as `std::collections::HashMap::insert()`.
|
|
|
#[cfg(test)]
|
|
|
|
|
|
pub fn insert(&mut self, name: String, room: Room) -> Option<Room> {
|
|
|
pub fn insert(&mut self, name: String, room: Room) -> Option<Room> {
|
|
|
self.map.insert(name, room)
|
|
|
self.map.insert(name, room)
|
|
|
}
|
|
|
}
|
|
|
@ -218,7 +75,7 @@ impl RoomMap { |
|
|
room
|
|
|
room
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
};
|
|
|
if let Some(_) = self.map.insert(name, room) {
|
|
|
|
|
|
|
|
|
if let Some(_) = self.insert(name, room) {
|
|
|
error!("Room present twice in room list response");
|
|
|
error!("Room present twice in room list response");
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@ -403,7 +260,9 @@ mod tests { |
|
|
|
|
|
|
|
|
use solstice_proto::server::RoomListResponse;
|
|
|
use solstice_proto::server::RoomListResponse;
|
|
|
|
|
|
|
|
|
use super::{Membership, Message, MessageHistory, Room, RoomMap, Visibility};
|
|
|
|
|
|
|
|
|
use crate::room::{Membership, Message, MessageHistory, Room, Visibility};
|
|
|
|
|
|
|
|
|
|
|
|
use super::RoomMap;
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
#[test]
|
|
|
fn deserialize_membership() {
|
|
|
fn deserialize_membership() {
|