From 92e3db274d9fa0a9242a0b1a70b2bbae3d1a36df Mon Sep 17 00:00:00 2001 From: Titouan Rigoudy Date: Tue, 12 Apr 2016 16:03:54 +0200 Subject: [PATCH] Store received messages, refactor joining code into room module. --- TODO.md | 1 + src/client.rs | 63 +++++++++----------- src/control/response.rs | 6 ++ src/room.rs | 124 ++++++++++++++++++++++++++++++---------- 4 files changed, 127 insertions(+), 67 deletions(-) diff --git a/TODO.md b/TODO.md index 638ddb4..103a0f0 100644 --- a/TODO.md +++ b/TODO.md @@ -3,3 +3,4 @@ Things to do: - Print out surplus bytes in hex to make analyzing them easier - Handle client connections + - Rename SayRoomResponse to RoomMessageResponse diff --git a/src/client.rs b/src/client.rs index 1f08932..5309128 100644 --- a/src/client.rs +++ b/src/client.rs @@ -210,7 +210,9 @@ impl Client { fn handle_room_list_request(&mut self) { // First send the controller client what we have in memory. - let response = self.rooms.get_room_list_response(); + let response = control::RoomListResponse { + rooms: self.rooms.get_room_list(), + }; self.control_send(control::Response::RoomListResponse(response)); // Then ask the server for an updated version, which will be forwarded // to the controller client once received. @@ -258,43 +260,21 @@ impl Client { } fn handle_join_room_response(&mut self, mut response: JoinRoomResponse) { - // First look up the room struct. - let room = match self.rooms.get_mut(&response.room_name) { - Some(room) => room, - None => { - error!( - "Received JoinRoomResponse for inexistent room \"{}\"", - &response.room_name - ); - return; - } - }; - - // Log what's happening. - if let room::Membership::Joining = room.membership { - info!("Joined room \"{}\"", response.room_name); - } else { - warn!( - "Joined room \"{}\" but membership was already {:?}", - response.room_name, room.membership - ); - } - - // Update the room struct. - room.membership = room::Membership::Member; - room.user_count = response.users.len(); - room.owner = response.owner; - for user_name in response.operators.drain(..) { - room.operators.insert(user_name.clone()); - } - for &(ref user_name, _) in response.users.iter() { - room.members.insert(user_name.clone()); - } + // Join the room and store the received information. + self.rooms.join( + &response.room_name, response.owner, response.operators, + &response.users); // Then update the user structs based on the info we just got. for (name, user) in response.users.drain(..) { self.users.insert(name, user); } + + let control_response = control::JoinRoomResponse { + room_name: response.room_name + }; + self.control_send(control::Response::JoinRoomResponse( + control_response)); } fn handle_login_response(&mut self, login: LoginResponse) { @@ -337,15 +317,21 @@ impl Client { fn handle_room_list_response(&mut self, response: RoomListResponse) { // Update the room map in memory. - self.rooms.update(response); + self.rooms.set_room_list(response); // Send the updated version to the controller. - let control_response = self.rooms.get_room_list_response(); + let control_response = control::RoomListResponse { + rooms: self.rooms.get_room_list(), + }; self.control_send( control::Response::RoomListResponse(control_response)); } fn handle_say_room_response(&mut self, response: SayRoomResponse) { - // TODO Save the message somewhere. + self.rooms.add_message(&response.room_name, room::Message { + user_name: response.user_name.clone(), + message: response.message.clone(), + }); + let control_response = control::SayRoomResponse { room_name: response.room_name, user_name: response.user_name, @@ -360,6 +346,11 @@ impl Client { if let Some(room) = self.rooms.get_mut(&response.room_name) { room.members.insert(response.user_name.clone()); self.users.insert(response.user_name, response.user); + } else { + error!( + "UserJoinedRoomResponse: unknown room \"{}\"", + response.room_name + ); } } } diff --git a/src/control/response.rs b/src/control/response.rs index b1222a1..d13d623 100644 --- a/src/control/response.rs +++ b/src/control/response.rs @@ -4,11 +4,17 @@ use room; /// to the controller. #[derive(Debug, RustcDecodable, RustcEncodable)] pub enum Response { + JoinRoomResponse(JoinRoomResponse), LoginStatusResponse(LoginStatusResponse), RoomListResponse(RoomListResponse), SayRoomResponse(SayRoomResponse), } +#[derive(Debug, RustcEncodable, RustcDecodable)] +pub struct JoinRoomResponse { + pub room_name: String, +} + /// This enumeration is the list of possible login states, and the associated /// information. #[derive(Debug, RustcDecodable, RustcEncodable)] diff --git a/src/room.rs b/src/room.rs index f372a57..b161ed1 100644 --- a/src/room.rs +++ b/src/room.rs @@ -3,6 +3,7 @@ use std::mem; use control; use proto::server; +use user; /// This enumeration is the list of possible membership states for a chat room. #[derive(Clone, Copy, Debug, RustcDecodable, RustcEncodable)] @@ -31,6 +32,13 @@ pub enum Visibility { PrivateOther, } +/// This structure contains a chat room message. +#[derive(Clone, Debug, RustcDecodable, RustcEncodable)] +pub struct Message { + pub user_name: String, + pub message: String, +} + /// 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. @@ -51,6 +59,8 @@ pub struct Room { pub operators: collections::HashSet, /// The names of the room's members. pub members: collections::HashSet, + /// The messages sent to this chat room, in chronological order. + pub messages: Vec, } impl Room { @@ -64,14 +74,9 @@ impl Room { owner: None, operators: collections::HashSet::new(), members: collections::HashSet::new(), + messages: Vec::new(), } } - - /// Merges the previous version of the room's information into the new - /// version. - fn merge(&mut self, old_room: &Self) { - self.membership = old_room.membership; - } } /// Contains the mapping from room names to room data and provides a clean @@ -102,63 +107,120 @@ impl RoomMap { self.map.get_mut(name) } - /// Updates one room in the mapping. + /// Updates one room in the map based on the information received in + /// a RoomListResponse and the potential previously stored information. fn update_one( - &mut self, name: String, mut new_room: Room, - old_map: & collections::HashMap) + &mut self, name: String, visibility: Visibility, user_count: u32, + old_map: &mut collections::HashMap) { - if let Some(old_room) = old_map.get(&name) { - new_room.merge(old_room); - } - if let Some(_) = self.map.insert(name, new_room) { + let room = match old_map.remove(&name) { + None => Room::new(Visibility::Public, user_count as usize), + Some(mut room) => { + room.visibility = visibility; + room.user_count = user_count as usize; + room + } + }; + if let Some(_) = self.map.insert(name, room) { error!("Room present twice in room list response"); } } /// Updates the map to reflect the information contained in the given /// server response. - pub fn update(&mut self, mut response: server::RoomListResponse) { + pub fn set_room_list(&mut self, mut response: server::RoomListResponse) { // Replace the old mapping with an empty one. - let old_map = mem::replace(&mut self.map, collections::HashMap::new()); + let mut old_map = + mem::replace(&mut self.map, collections::HashMap::new()); // Add all public rooms. for (name, user_count) in response.rooms.drain(..) { - let new_room = Room::new(Visibility::Public, user_count as usize); - self.update_one(name, new_room, &old_map); + self.update_one( + name, Visibility::Public, user_count, &mut old_map); } // Add all private, owned, rooms. for (name, user_count) in response.owned_private_rooms.drain(..) { - let new_room = Room::new( - Visibility::PrivateOwned, user_count as usize); - self.update_one(name, new_room, &old_map); + self.update_one( + name, Visibility::PrivateOwned, user_count, &mut old_map); } // Add all private, unowned, rooms. for (name, user_count) in response.other_private_rooms.drain(..) { - let new_room = Room::new( - Visibility::PrivateOther, user_count as usize); - self.update_one(name, new_room, &old_map); + self.update_one( + name, Visibility::PrivateOther, user_count, &mut old_map); } // Mark all operated rooms as necessary. - for name in response.operated_private_room_names.drain(..) { - match self.map.get_mut(&name) { + for name in response.operated_private_room_names.iter() { + match self.map.get_mut(name) { Some(room) => room.operated = true, None => error!("Room {} is operated but does not exist", name), } } } - /// Creates a control response containing the list of visible rooms. - pub fn get_room_list_response(&self) - -> control::RoomListResponse + /// Returns the list of (room name, room data) representing all known rooms. + pub fn get_room_list(&self) -> Vec<(String, Room)> { - let mut response = control::RoomListResponse{ rooms: Vec::new() }; + let mut rooms = Vec::new(); for (room_name, room) in self.map.iter() { - response.rooms.push((room_name.clone(), room.clone())); + rooms.push((room_name.clone(), room.clone())); + } + rooms + } + + pub fn join( + &mut self, room_name: &str, + owner: Option, + mut operators: Vec, + members: &Vec<(String, user::User)>) + { + // First look up the room struct. + let room = match self.map.get_mut(room_name) { + Some(room) => room, + None => { + error!( + "RoomMap::join: unknown room \"{}\"", + room_name + ); + return; + } + }; + + // Log what's happening. + if let Membership::Joining = room.membership { + info!("Joined room \"{}\"", room_name); + } else { + warn!( + "Joined room \"{}\" but membership was already {:?}", + room_name, room.membership + ); + } + + // Update the room struct. + room.membership = Membership::Member; + room.user_count = members.len(); + room.owner = owner; + for user_name in operators.drain(..) { + room.operators.insert(user_name); + } + for &(ref user_name, _) in members.iter() { + room.members.insert(user_name.clone()); + } + } + + /// Saves the given message as the last one in the given room. + pub fn add_message(&mut self, room_name: &str, message: Message) { + match self.get_mut(room_name) { + None => { + error!( + "SayRoomResponse: unknown room \"{}\"", room_name + ); + return; + }, + Some(room) => room.messages.push(message), } - response } }