|
|
|
@ -5,7 +5,7 @@ use log::{error, info, warn}; |
|
|
|
use solstice_proto::{server, User};
|
|
|
|
use thiserror::Error;
|
|
|
|
|
|
|
|
use crate::room::{RoomMembership, RoomState, RoomVisibility};
|
|
|
|
use crate::room::{RoomMembership, RoomMessage, RoomState, RoomVisibility};
|
|
|
|
|
|
|
|
/// The error returned by RoomMap functions.
|
|
|
|
#[derive(Debug, Error)]
|
|
|
|
@ -21,12 +21,30 @@ pub enum RoomError { |
|
|
|
#[error("room {0} not found")]
|
|
|
|
pub struct RoomNotFoundError(String);
|
|
|
|
|
|
|
|
/// An entry in the chat room map.
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct RoomEntry {
|
|
|
|
state: RoomState,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl RoomEntry {
|
|
|
|
/// Returns a copy of the room state contained in this entry.
|
|
|
|
pub fn clone_state(&self) -> RoomState {
|
|
|
|
self.state.clone()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Inserts the given message in this chat room's history.
|
|
|
|
pub fn insert_message(&mut self, message: RoomMessage) {
|
|
|
|
self.state.messages.insert(message)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Contains the mapping from room names to room data and provides a clean
|
|
|
|
/// interface to interact with it.
|
|
|
|
#[derive(Debug, Default)]
|
|
|
|
pub struct RoomMap {
|
|
|
|
/// The actual map from room names to room data.
|
|
|
|
map: HashMap<String, RoomState>,
|
|
|
|
map: HashMap<String, RoomEntry>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl RoomMap {
|
|
|
|
@ -38,7 +56,10 @@ impl RoomMap { |
|
|
|
/// Inserts the given room in the map under the given name.
|
|
|
|
/// Same semantics as `std::collections::HashMap::insert()`.
|
|
|
|
pub fn insert(&mut self, name: String, room: RoomState) -> Option<RoomState> {
|
|
|
|
self.map.insert(name, room)
|
|
|
|
self
|
|
|
|
.map
|
|
|
|
.insert(name, RoomEntry { state: room })
|
|
|
|
.map(|entry| entry.state)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Looks up the given room name in the map, returning an immutable
|
|
|
|
@ -46,7 +67,7 @@ impl RoomMap { |
|
|
|
pub fn get_strict(
|
|
|
|
&self,
|
|
|
|
room_name: &str,
|
|
|
|
) -> Result<&RoomState, RoomNotFoundError> {
|
|
|
|
) -> Result<&RoomEntry, RoomNotFoundError> {
|
|
|
|
match self.map.get(room_name) {
|
|
|
|
Some(room) => Ok(room),
|
|
|
|
None => Err(RoomNotFoundError(room_name.to_string())),
|
|
|
|
@ -58,7 +79,7 @@ impl RoomMap { |
|
|
|
pub fn get_mut_strict(
|
|
|
|
&mut self,
|
|
|
|
room_name: &str,
|
|
|
|
) -> Result<&mut RoomState, RoomNotFoundError> {
|
|
|
|
) -> Result<&mut RoomEntry, RoomNotFoundError> {
|
|
|
|
match self.map.get_mut(room_name) {
|
|
|
|
Some(room) => Ok(room),
|
|
|
|
None => Err(RoomNotFoundError(room_name.to_string())),
|
|
|
|
@ -72,14 +93,14 @@ impl RoomMap { |
|
|
|
name: String,
|
|
|
|
visibility: RoomVisibility,
|
|
|
|
user_count: u32,
|
|
|
|
old_map: &mut HashMap<String, RoomState>,
|
|
|
|
old_map: &mut HashMap<String, RoomEntry>,
|
|
|
|
) {
|
|
|
|
let room = match old_map.remove(&name) {
|
|
|
|
None => RoomState::new(RoomVisibility::Public, user_count as usize),
|
|
|
|
Some(mut room) => {
|
|
|
|
room.visibility = visibility;
|
|
|
|
room.user_count = user_count as usize;
|
|
|
|
room
|
|
|
|
Some(RoomEntry { mut state }) => {
|
|
|
|
state.visibility = visibility;
|
|
|
|
state.user_count = user_count as usize;
|
|
|
|
state
|
|
|
|
}
|
|
|
|
};
|
|
|
|
if let Some(_) = self.insert(name, room) {
|
|
|
|
@ -121,7 +142,7 @@ impl RoomMap { |
|
|
|
// Mark all operated rooms as necessary.
|
|
|
|
for name in response.operated_private_room_names.iter() {
|
|
|
|
match self.map.get_mut(name) {
|
|
|
|
Some(room) => room.operated = true,
|
|
|
|
Some(room) => room.state.operated = true,
|
|
|
|
None => error!("Room {} is operated but does not exist", name),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -131,7 +152,7 @@ impl RoomMap { |
|
|
|
pub fn get_room_list(&self) -> Vec<(String, RoomState)> {
|
|
|
|
let mut rooms = Vec::new();
|
|
|
|
for (room_name, room) in self.map.iter() {
|
|
|
|
rooms.push((room_name.clone(), room.clone()));
|
|
|
|
rooms.push((room_name.clone(), room.clone_state()));
|
|
|
|
}
|
|
|
|
rooms
|
|
|
|
}
|
|
|
|
@ -142,9 +163,9 @@ impl RoomMap { |
|
|
|
pub fn start_joining(&mut self, room_name: &str) -> Result<(), RoomError> {
|
|
|
|
let room = self.get_mut_strict(room_name)?;
|
|
|
|
|
|
|
|
match room.membership {
|
|
|
|
match room.state.membership {
|
|
|
|
RoomMembership::NonMember => {
|
|
|
|
room.membership = RoomMembership::Joining;
|
|
|
|
room.state.membership = RoomMembership::Joining;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
@ -163,33 +184,33 @@ impl RoomMap { |
|
|
|
owner: Option<String>,
|
|
|
|
mut operators: Vec<String>,
|
|
|
|
members: &[User],
|
|
|
|
) -> Result<&RoomState, RoomError> {
|
|
|
|
) -> Result<&RoomEntry, RoomError> {
|
|
|
|
// First look up the room struct.
|
|
|
|
let room = self.get_mut_strict(room_name)?;
|
|
|
|
|
|
|
|
// Log what's happening.
|
|
|
|
if let RoomMembership::Joining = room.membership {
|
|
|
|
if let RoomMembership::Joining = room.state.membership {
|
|
|
|
info!("Joined room {:?}", room_name);
|
|
|
|
} else {
|
|
|
|
warn!(
|
|
|
|
"Joined room {:?} but membership was already {:?}",
|
|
|
|
room_name, room.membership
|
|
|
|
room_name, room.state.membership
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the room struct.
|
|
|
|
room.membership = RoomMembership::Member;
|
|
|
|
room.user_count = members.len();
|
|
|
|
room.owner = owner;
|
|
|
|
// Update the room state.
|
|
|
|
room.state.membership = RoomMembership::Member;
|
|
|
|
room.state.user_count = members.len();
|
|
|
|
room.state.owner = owner;
|
|
|
|
|
|
|
|
room.operators.clear();
|
|
|
|
room.state.operators.clear();
|
|
|
|
for user_name in operators.drain(..) {
|
|
|
|
room.operators.insert(user_name);
|
|
|
|
room.state.operators.insert(user_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
room.members.clear();
|
|
|
|
room.state.members.clear();
|
|
|
|
for user in members {
|
|
|
|
room.members.insert(user.name.clone());
|
|
|
|
room.state.members.insert(user.name.clone());
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(room)
|
|
|
|
@ -202,9 +223,9 @@ impl RoomMap { |
|
|
|
pub fn start_leaving(&mut self, room_name: &str) -> Result<(), RoomError> {
|
|
|
|
let room = self.get_mut_strict(room_name)?;
|
|
|
|
|
|
|
|
match room.membership {
|
|
|
|
match room.state.membership {
|
|
|
|
RoomMembership::Member => {
|
|
|
|
room.membership = RoomMembership::Leaving;
|
|
|
|
room.state.membership = RoomMembership::Leaving;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
@ -219,7 +240,7 @@ impl RoomMap { |
|
|
|
pub fn leave(&mut self, room_name: &str) -> Result<(), RoomError> {
|
|
|
|
let room = self.get_mut_strict(room_name)?;
|
|
|
|
|
|
|
|
match room.membership {
|
|
|
|
match room.state.membership {
|
|
|
|
RoomMembership::Leaving => info!("Left room {:?}", room_name),
|
|
|
|
|
|
|
|
membership => warn!(
|
|
|
|
@ -228,7 +249,7 @@ impl RoomMap { |
|
|
|
),
|
|
|
|
}
|
|
|
|
|
|
|
|
room.membership = RoomMembership::NonMember;
|
|
|
|
room.state.membership = RoomMembership::NonMember;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
@ -240,7 +261,7 @@ impl RoomMap { |
|
|
|
user_name: String,
|
|
|
|
) -> Result<(), RoomError> {
|
|
|
|
let room = self.get_mut_strict(room_name)?;
|
|
|
|
room.members.insert(user_name);
|
|
|
|
room.state.members.insert(user_name);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
@ -252,7 +273,7 @@ impl RoomMap { |
|
|
|
user_name: &str,
|
|
|
|
) -> Result<(), RoomError> {
|
|
|
|
let room = self.get_mut_strict(room_name)?;
|
|
|
|
room.members.remove(user_name);
|
|
|
|
room.state.members.remove(user_name);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
@ -266,7 +287,7 @@ impl RoomMap { |
|
|
|
tickers: Vec<(String, String)>,
|
|
|
|
) -> Result<(), RoomError> {
|
|
|
|
let room = self.get_mut_strict(room_name)?;
|
|
|
|
room.tickers = tickers;
|
|
|
|
room.state.tickers = tickers;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -295,8 +316,8 @@ mod tests { |
|
|
|
});
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
rooms.get_strict("room a").unwrap(),
|
|
|
|
&RoomState::new(RoomVisibility::Public, 42)
|
|
|
|
rooms.get_strict("room a").unwrap().clone_state(),
|
|
|
|
RoomState::new(RoomVisibility::Public, 42)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|