Browse Source

Store received messages, refactor joining code into room module.

wip
Titouan Rigoudy 9 years ago
parent
commit
92e3db274d
4 changed files with 127 additions and 67 deletions
  1. +1
    -0
      TODO.md
  2. +27
    -36
      src/client.rs
  3. +6
    -0
      src/control/response.rs
  4. +93
    -31
      src/room.rs

+ 1
- 0
TODO.md View File

@ -3,3 +3,4 @@ Things to do:
- Print out surplus bytes in hex to make analyzing them easier - Print out surplus bytes in hex to make analyzing them easier
- Handle client connections - Handle client connections
- Rename SayRoomResponse to RoomMessageResponse

+ 27
- 36
src/client.rs View File

@ -210,7 +210,9 @@ impl Client {
fn handle_room_list_request(&mut self) { fn handle_room_list_request(&mut self) {
// First send the controller client what we have in memory. // 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)); self.control_send(control::Response::RoomListResponse(response));
// Then ask the server for an updated version, which will be forwarded // Then ask the server for an updated version, which will be forwarded
// to the controller client once received. // to the controller client once received.
@ -258,43 +260,21 @@ impl Client {
} }
fn handle_join_room_response(&mut self, mut response: JoinRoomResponse) { 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. // Then update the user structs based on the info we just got.
for (name, user) in response.users.drain(..) { for (name, user) in response.users.drain(..) {
self.users.insert(name, user); 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) { fn handle_login_response(&mut self, login: LoginResponse) {
@ -337,15 +317,21 @@ impl Client {
fn handle_room_list_response(&mut self, response: RoomListResponse) { fn handle_room_list_response(&mut self, response: RoomListResponse) {
// Update the room map in memory. // Update the room map in memory.
self.rooms.update(response);
self.rooms.set_room_list(response);
// Send the updated version to the controller. // 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( self.control_send(
control::Response::RoomListResponse(control_response)); control::Response::RoomListResponse(control_response));
} }
fn handle_say_room_response(&mut self, response: SayRoomResponse) { 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 { let control_response = control::SayRoomResponse {
room_name: response.room_name, room_name: response.room_name,
user_name: response.user_name, user_name: response.user_name,
@ -360,6 +346,11 @@ impl Client {
if let Some(room) = self.rooms.get_mut(&response.room_name) { if let Some(room) = self.rooms.get_mut(&response.room_name) {
room.members.insert(response.user_name.clone()); room.members.insert(response.user_name.clone());
self.users.insert(response.user_name, response.user); self.users.insert(response.user_name, response.user);
} else {
error!(
"UserJoinedRoomResponse: unknown room \"{}\"",
response.room_name
);
} }
} }
} }

+ 6
- 0
src/control/response.rs View File

@ -4,11 +4,17 @@ use room;
/// to the controller. /// to the controller.
#[derive(Debug, RustcDecodable, RustcEncodable)] #[derive(Debug, RustcDecodable, RustcEncodable)]
pub enum Response { pub enum Response {
JoinRoomResponse(JoinRoomResponse),
LoginStatusResponse(LoginStatusResponse), LoginStatusResponse(LoginStatusResponse),
RoomListResponse(RoomListResponse), RoomListResponse(RoomListResponse),
SayRoomResponse(SayRoomResponse), 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 /// This enumeration is the list of possible login states, and the associated
/// information. /// information.
#[derive(Debug, RustcDecodable, RustcEncodable)] #[derive(Debug, RustcDecodable, RustcEncodable)]


+ 93
- 31
src/room.rs View File

@ -3,6 +3,7 @@ use std::mem;
use control; use control;
use proto::server; use proto::server;
use user;
/// This enumeration is the list of possible membership states for a chat room. /// This enumeration is the list of possible membership states for a chat room.
#[derive(Clone, Copy, Debug, RustcDecodable, RustcEncodable)] #[derive(Clone, Copy, Debug, RustcDecodable, RustcEncodable)]
@ -31,6 +32,13 @@ pub enum Visibility {
PrivateOther, 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. /// 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 /// It does not store the name, as that is stored implicitly as the key in the
/// room hash table. /// room hash table.
@ -51,6 +59,8 @@ pub struct Room {
pub operators: collections::HashSet<String>, pub operators: collections::HashSet<String>,
/// The names of the room's members. /// The names of the room's members.
pub members: collections::HashSet<String>, pub members: collections::HashSet<String>,
/// The messages sent to this chat room, in chronological order.
pub messages: Vec<Message>,
} }
impl Room { impl Room {
@ -64,14 +74,9 @@ impl Room {
owner: None, owner: None,
operators: collections::HashSet::new(), operators: collections::HashSet::new(),
members: 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 /// Contains the mapping from room names to room data and provides a clean
@ -102,63 +107,120 @@ impl RoomMap {
self.map.get_mut(name) 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( fn update_one(
&mut self, name: String, mut new_room: Room,
old_map: & collections::HashMap<String, Room>)
&mut self, name: String, visibility: Visibility, user_count: u32,
old_map: &mut collections::HashMap<String, Room>)
{ {
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"); error!("Room present twice in room list response");
} }
} }
/// Updates the map to reflect the information contained in the given /// Updates the map to reflect the information contained in the given
/// server response. /// 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. // 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. // Add all public rooms.
for (name, user_count) in response.rooms.drain(..) { 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. // Add all private, owned, rooms.
for (name, user_count) in response.owned_private_rooms.drain(..) { 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. // Add all private, unowned, rooms.
for (name, user_count) in response.other_private_rooms.drain(..) { 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. // 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, Some(room) => room.operated = true,
None => error!("Room {} is operated but does not exist", name), 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() { 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<String>,
mut operators: Vec<String>,
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
} }
} }

Loading…
Cancel
Save