diff --git a/src/client.rs b/src/client.rs index 3671d41..7b21db0 100644 --- a/src/client.rs +++ b/src/client.rs @@ -144,8 +144,42 @@ impl Client { } fn handle_join_room_request(&mut self, room_name: String) { - let request = JoinRoomRequest { room_name: room_name }; - self.server_send(ServerRequest::JoinRoomRequest(request)); + // First check that we are not already a member. + // Enclosed in a block otherwise the mutable borrow of self.rooms + // prevents from calling self.server_send. + { + let room = match self.rooms.get_mut(&room_name) { + Some(room) => room, + None => { + error!("Cannot join inexistent room \"{}\"", room_name); + return; + } + }; + match room.membership { + room::Membership::NonMember => { + // As expected. + room.membership = room::Membership::Joining; + }, + + room::Membership::Member => { + warn!( + "Controller requested to re-join room \"{}\"", + room_name + ); + // TODO Send control response + }, + + membership => { + error!( + "Cannot join room \"{}\": current membership: {:?}", + room_name, membership + ); + } + } + } + self.server_send(ServerRequest::JoinRoomRequest(JoinRoomRequest { + room_name: room_name + })); } fn handle_login_status_request(&mut self) { @@ -185,6 +219,9 @@ impl Client { fn handle_server_response(&mut self, response: ServerResponse) { match response { + ServerResponse::JoinRoomResponse(response) => + self.handle_join_room_response(response), + ServerResponse::LoginResponse(response) => self.handle_login_response(response), @@ -201,6 +238,21 @@ impl Client { } } + fn handle_join_room_response(&mut self, response: JoinRoomResponse) { + let room = match self.rooms.get_mut(&response.room_name) { + Some(room) => room, + None => { + error!( + "Received JoinRoomResponse for inexistent room \"{}\"", + &response.room_name + ); + return; + } + }; + room.membership = room::Membership::Member; + room.user_count = response.user_names.len(); + } + fn handle_login_response(&mut self, login: LoginResponse) { if let LoginStatus::Pending = self.login_status { match login { @@ -234,7 +286,11 @@ impl Client { } fn handle_room_list_response(&mut self, response: RoomListResponse) { + // Update the room map in memory. self.rooms.update(response); + // Send the updated version to the controller. + let response = self.rooms.get_room_list_response(); + self.control_send(control::Response::RoomListResponse(response)); } fn handle_privileged_users_response( diff --git a/src/control/controller.rs b/src/control/controller.rs index 7c124e2..e8146b1 100644 --- a/src/control/controller.rs +++ b/src/control/controller.rs @@ -182,10 +182,14 @@ impl Controller { } }, - websocket::message::Type::Close => break, + websocket::message::Type::Close => { + info!("Received close request from controller."); + break; + }, - code => warn!("Unhandled websocket message with code {:?}", - code), + code => warn!( + "Unhandled websocket message with code {:?}", code + ), } } info!("Shutting down websocket receiver"); @@ -201,6 +205,7 @@ impl Controller { { let payload = try!(str::from_utf8(payload_bytes)); let control_request = try!(json::decode(payload)); + debug!("Received control request: {:?}", control_request); try!(client_tx.send(control_request)); Ok(()) } @@ -237,6 +242,7 @@ impl Controller { fn send_response(sender: &mut WebSocketSender, response: Response) -> Result<(), Error> { + debug!("Sending control response: {:?}", response); let encoded = try!(json::encode(&response)); let message = websocket::Message::text(encoded); try!(sender.send_message(&message)); diff --git a/src/handler.rs b/src/handler.rs index 118d59c..b32fc89 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -111,6 +111,8 @@ impl ConnectionHandler { }; let server_response = try!(ServerResponse::from_packet(&mut packet)); + debug!("Received server response: {:?}", server_response); + match self.client_tx.send(Response::ServerResponse(server_response)) { Ok(()) => Ok(true), Err(e) => Err(io::Error::new( @@ -136,6 +138,7 @@ impl ConnectionHandler { } fn notify_server(&mut self, request: ServerRequest) -> io::Result<()> { + debug!("Sending server request: {:?}", request); let packet = try!(request.to_packet()); self.server_queue.push_back(packet); Ok(()) diff --git a/src/main.rs b/src/main.rs index f6e9127..9814f17 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ mod control; mod handler; mod proto; mod room; +mod user; extern crate byteorder; extern crate core; diff --git a/src/proto/server/response.rs b/src/proto/server/response.rs index de7f08c..38eb4d2 100644 --- a/src/proto/server/response.rs +++ b/src/proto/server/response.rs @@ -4,6 +4,8 @@ use std::net; use super::constants::*; use super::super::packet::Packet; +use user; + /*=============* * FROM PACKET * *=============*/ @@ -135,26 +137,37 @@ impl FromPacket for ConnectToPeerResponse { #[derive(Debug)] pub struct JoinRoomResponse { - room_name: String, - user_names: Vec, - user_statuses: Vec, + pub room_name: String, + pub user_names: Vec, + pub user_infos: Vec, + pub user_countries: Vec, + pub owner_and_operators: Option<(String, Vec)>, } impl FromPacket for JoinRoomResponse { fn from_packet(packet: &mut Packet) -> io::Result { - let room_name = try!(packet.read_str()); + let mut response = JoinRoomResponse { + room_name: try!(packet.read_str()), + user_names: Vec::new(), + user_infos: Vec::new(), + user_countries: Vec::new(), + owner_and_operators: None, + }; - let mut user_names = Vec::new(); - try!(packet.read_array(&mut user_names, Packet::read_str)); + try!(packet.read_array(&mut response.user_names, Packet::read_str)); - let mut user_statuses = Vec::new(); - try!(packet.read_array(&mut user_statuses, Packet::read_uint)); + try!(response.read_user_infos(packet)); - Ok(JoinRoomResponse { - room_name: room_name, - user_names: user_names, - user_statuses: user_statuses, - }) + try!(packet.read_array(&mut response.user_countries, Packet::read_str)); + + Ok(response) + } +} + +impl JoinRoomResponse { + fn read_user_infos(&mut self, packet: &mut Packet) -> io::Result<()> + { + Ok(()) } } diff --git a/src/room.rs b/src/room.rs index 3b1b252..eee3240 100644 --- a/src/room.rs +++ b/src/room.rs @@ -1,8 +1,24 @@ use std::collections; +use std::mem; use control; use proto::server; +/// This enumeration is the list of possible membership states for a chat room. +#[derive(Clone, Copy, Debug, RustcDecodable, RustcEncodable)] +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, RustcDecodable, RustcEncodable)] @@ -20,6 +36,8 @@ pub enum Visibility { /// room hash table. #[derive(Clone, Copy, Debug, RustcDecodable, RustcEncodable)] 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 @@ -29,6 +47,14 @@ pub struct Room { pub user_count: usize, } +impl Room { + /// 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 /// interface to interact with it. #[derive(Debug)] @@ -51,56 +77,69 @@ impl RoomMap { self.map.get(name) } - /// Looks up the given room name in the map, returning a mutable reference - /// to the associated data if found. + /// Looks up the given room name in the map, returning a mutable + /// reference to the associated data if found. pub fn get_mut(&mut self, name: &str) -> Option<&mut Room> { self.map.get_mut(name) } + /// Updates one room in the mapping. + fn update_one( + &mut self, name: String, mut new_room: Room, + old_map: & 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) { + 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) { - // First, clear the current map, keeping backing memory. - self.map.clear(); + // Replace the old mapping with an empty one. + let old_map = mem::replace(&mut self.map, collections::HashMap::new()); // Add all public rooms. for (name, user_count) in response.rooms.drain(..) { - self.map.insert(name, Room { + let new_room = Room { + membership: Membership::NonMember, visibility: Visibility::Public, operated: false, user_count: user_count as usize, - }); + }; + self.update_one(name, new_room, &old_map); } // Add all private, owned, rooms. for (name, user_count) in response.owned_private_rooms.drain(..) { - let room = Room { + let new_room = Room { + membership: Membership::NonMember, visibility: Visibility::PrivateOwned, operated: false, user_count: user_count as usize, }; - if let Some(_) = self.map.insert(name, room) { - error!("Room is both public and owned_private"); - } + self.update_one(name, new_room, &old_map); } // Add all private, unowned, rooms. for (name, user_count) in response.other_private_rooms.drain(..) { - let room = Room { + let new_room = Room { + membership: Membership::NonMember, visibility: Visibility::PrivateOther, operated: false, user_count: user_count as usize, }; - if let Some(_) = self.map.insert(name, room) { - error!("Room is both public and other_private"); - } + self.update_one(name, new_room, &old_map); } // Mark all operated rooms as necessary. for name in response.operated_private_room_names.drain(..) { match self.map.get_mut(&name) { - None => error!("Room {} is operated but does not exist", name), Some(room) => room.operated = true, + None => error!("Room {} is operated but does not exist", name), } } } @@ -116,3 +155,4 @@ impl RoomMap { response } } +