From 56c170c119f6425cc080cbf97c408e4717dad146 Mon Sep 17 00:00:00 2001 From: Titouan Rigoudy Date: Thu, 7 Apr 2016 14:44:42 +0200 Subject: [PATCH] Refactor Error into result module, use it. --- src/control/controller.rs | 78 ++-------------------- src/handler.rs | 13 ++-- src/main.rs | 1 + src/proto/packet.rs | 13 ++-- src/proto/server/response.rs | 90 ++++++++++++++++++++------ src/result.rs | 121 +++++++++++++++++++++++++++++++++++ src/user.rs | 35 ++++++++-- 7 files changed, 241 insertions(+), 110 deletions(-) create mode 100644 src/result.rs diff --git a/src/control/controller.rs b/src/control/controller.rs index e8146b1..db0cd7c 100644 --- a/src/control/controller.rs +++ b/src/control/controller.rs @@ -9,6 +9,7 @@ use websocket; use websocket::{Receiver, Sender}; use config; +use result; use super::request::*; use super::response::*; @@ -22,71 +23,6 @@ type WebSocketSender = type WebSocketClient = websocket::Client; -#[derive(Debug)] -enum Error { - IOError(io::Error), - JSONEncoderError(json::EncoderError), - JSONDecoderError(json::DecoderError), - SendError(mpsc::SendError), - Utf8Error(str::Utf8Error), - WebSocketError(websocket::result::WebSocketError), -} - -impl fmt::Display for Error { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - match *self { - Error::IOError(ref err) => - write!(fmt, "IOError({})", err), - Error::JSONEncoderError(ref err) => - write!(fmt, "JSONEncoderError({})", err), - Error::JSONDecoderError(ref err) => - write!(fmt, "JSONDecoderError({})", err), - Error::SendError(ref err) => - write!(fmt, "SendError({})", err), - Error::Utf8Error(ref err) => - write!(fmt, "Utf8Error({})", err), - Error::WebSocketError(ref err) => - write!(fmt, "WebSocketError({})", err), - } - } -} - -impl From for Error { - fn from(err: io::Error) -> Self { - Error::IOError(err) - } -} - -impl From for Error { - fn from(err: json::EncoderError) -> Self { - Error::JSONEncoderError(err) - } -} - -impl From for Error { - fn from(err: json::DecoderError) -> Self { - Error::JSONDecoderError(err) - } -} - -impl From> for Error { - fn from(err: mpsc::SendError) -> Self { - Error::SendError(err) - } -} - -impl From for Error { - fn from(err: str::Utf8Error) -> Self { - Error::Utf8Error(err) - } -} - -impl From for Error { - fn from(err: websocket::result::WebSocketError) -> Self { - Error::WebSocketError(err) - } -} - pub struct Controller { client_tx: mpsc::Sender, client_rx: mpsc::Receiver, @@ -148,14 +84,12 @@ impl Controller { } fn try_get_client(server: &mut websocket::Server) - -> io::Result + -> result::Result { let connection = try!(server.accept()); let request = try!(connection.read_request()); - match request.accept().send() { - Ok(client) => Ok(client), - Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)), - } + let client = try!(request.accept().send()); + Ok(client) } fn receiver_loop( @@ -201,7 +135,7 @@ impl Controller { fn handle_text_message( payload_bytes: &[u8], client_tx: &mpsc::Sender) - -> Result<(), Error> + -> result::Result<()> { let payload = try!(str::from_utf8(payload_bytes)); let control_request = try!(json::decode(payload)); @@ -240,7 +174,7 @@ impl Controller { } fn send_response(sender: &mut WebSocketSender, response: Response) - -> Result<(), Error> + -> result::Result<()> { debug!("Sending control response: {:?}", response); let encoded = try!(json::encode(&response)); diff --git a/src/handler.rs b/src/handler.rs index b32fc89..8728b84 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -8,6 +8,7 @@ use mio::tcp::TcpStream; use proto::{Packet, PacketStream, Request, Response}; use proto::server::*; +use result; struct TokenCounter { counter: usize, @@ -104,22 +105,18 @@ impl ConnectionHandler { } } - fn read_server_once(&mut self) -> io::Result { + fn read_server_once(&mut self) -> result::Result { let mut packet = match try!(self.server_stream.try_read()) { Some(packet) => packet, None => return Ok(false), }; + debug!("Read packet with size {}", packet.bytes_remaining()); 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( - io::ErrorKind::Other, - format!("Send failed on client_tx channel: {}", e))), - - } + try!(self.client_tx.send(Response::ServerResponse(server_response))); + Ok(true) } fn write_server_once(&mut self) -> io::Result { diff --git a/src/main.rs b/src/main.rs index 9814f17..997eb31 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ mod client; mod config; mod control; +mod result; mod handler; mod proto; mod room; diff --git a/src/proto/packet.rs b/src/proto/packet.rs index 4542382..00068b7 100644 --- a/src/proto/packet.rs +++ b/src/proto/packet.rs @@ -87,9 +87,10 @@ impl Packet { } } - pub fn read_array(&mut self, vector: &mut Vec, read_item: F) - -> io::Result - where F: Fn(&mut Self) -> io::Result + pub fn read_array(&mut self, vector: &mut Vec, read_item: F) + -> Result + where F: Fn(&mut Self) -> Result, + E: From { self.read_array_with(|packet, _| { let item = try!(read_item(packet)); @@ -98,8 +99,10 @@ impl Packet { }) } - pub fn read_array_with(&mut self, mut read_item: F) -> io::Result - where F: FnMut(&mut Self, usize) -> io::Result<()> + pub fn read_array_with(&mut self, mut read_item: F) + -> Result + where F: FnMut(&mut Self, usize) -> Result<(), E>, + E: From { let num_items = try!(self.read_uint()) as usize; for i in 0..num_items { diff --git a/src/proto/server/response.rs b/src/proto/server/response.rs index 38eb4d2..bb99d6d 100644 --- a/src/proto/server/response.rs +++ b/src/proto/server/response.rs @@ -4,6 +4,7 @@ use std::net; use super::constants::*; use super::super::packet::Packet; +use result; use user; /*=============* @@ -11,7 +12,7 @@ use user; *=============*/ pub trait FromPacket: Sized { - fn from_packet(&mut Packet) -> io::Result; + fn from_packet(&mut Packet) -> result::Result; } /*=================* @@ -36,7 +37,7 @@ pub enum ServerResponse { } impl FromPacket for ServerResponse { - fn from_packet(packet: &mut Packet) -> io::Result { + fn from_packet(packet: &mut Packet) -> result::Result { let code = try!(packet.read_uint()); let resp = match code { CODE_CONNECT_TO_PEER => @@ -110,7 +111,7 @@ pub struct ConnectToPeerResponse { } impl FromPacket for ConnectToPeerResponse { - fn from_packet(packet: &mut Packet) -> io::Result { + fn from_packet(packet: &mut Packet) -> result::Result { let username = try!(packet.read_str()); let connection_type = try!(packet.read_str()); @@ -145,7 +146,7 @@ pub struct JoinRoomResponse { } impl FromPacket for JoinRoomResponse { - fn from_packet(packet: &mut Packet) -> io::Result { + fn from_packet(packet: &mut Packet) -> result::Result { let mut response = JoinRoomResponse { room_name: try!(packet.read_str()), user_names: Vec::new(), @@ -165,8 +166,51 @@ impl FromPacket for JoinRoomResponse { } impl JoinRoomResponse { - fn read_user_infos(&mut self, packet: &mut Packet) -> io::Result<()> + fn read_user_infos(&mut self, packet: &mut Packet) + -> result::Result<()> { + let num_statuses_res: result::Result = + packet.read_array(&mut self.user_infos, |packet| { + let status_u32 = try!(packet.read_uint()); + let status = try!(user::Status::from_u32(status_u32)); + Ok(user::User { + status: status, + average_speed: 0, + num_downloads: 0, + unknown: 0, + num_files: 0, + num_folders: 0, + num_free_slots: 0, + }) + }); + let num_statuses = try!(num_statuses_res); + + let num_infos_res: result::Result = + packet.read_array_with(|packet, i| { + if let Some(user) = self.user_infos.get_mut(i) { + user.average_speed = try!(packet.read_uint()) as usize; + user.num_downloads = try!(packet.read_uint()) as usize; + user.unknown = try!(packet.read_uint()) as usize; + user.num_files = try!(packet.read_uint()) as usize; + user.num_folders = try!(packet.read_uint()) as usize; + } + Ok(()) + }); + let num_infos = try!(num_infos_res); + + let num_free_slots_res: result::Result = + packet.read_array_with(|packet, i| { + if let Some(user) = self.user_infos.get_mut(i) { + user.num_free_slots = try!(packet.read_uint()) as usize; + } + Ok(()) + }); + let num_free_slots = try!(num_free_slots_res); + + if num_statuses != num_infos || num_statuses != num_free_slots { + warn!("JoinRoomResponse: mismatched vector sizes"); + } + Ok(()) } } @@ -188,7 +232,7 @@ pub enum LoginResponse { } impl FromPacket for LoginResponse { - fn from_packet(packet: &mut Packet) -> io::Result { + fn from_packet(packet: &mut Packet) -> result::Result { let ok = try!(packet.read_bool()); if ok { let motd = try!(packet.read_str()); @@ -222,7 +266,7 @@ pub struct ParentMinSpeedResponse { } impl FromPacket for ParentMinSpeedResponse { - fn from_packet(packet: &mut Packet) -> io::Result { + fn from_packet(packet: &mut Packet) -> result::Result { let value = try!(packet.read_uint()); Ok(ParentMinSpeedResponse { value: value, @@ -240,7 +284,7 @@ pub struct ParentSpeedRatioResponse { } impl FromPacket for ParentSpeedRatioResponse { - fn from_packet(packet: &mut Packet) -> io::Result { + fn from_packet(packet: &mut Packet) -> result::Result { let value = try!(packet.read_uint()); Ok(ParentSpeedRatioResponse { value: value, @@ -260,7 +304,7 @@ pub struct PeerAddressResponse { } impl FromPacket for PeerAddressResponse { - fn from_packet(packet: &mut Packet) -> io::Result { + fn from_packet(packet: &mut Packet) -> result::Result { let username = try!(packet.read_str()); let ip = try!(packet.read_ipv4_addr()); let port = try!(packet.read_port()); @@ -283,7 +327,7 @@ pub struct PrivilegedUsersResponse { } impl FromPacket for PrivilegedUsersResponse { - fn from_packet(packet: &mut Packet) -> io::Result { + fn from_packet(packet: &mut Packet) -> result::Result { let mut response = PrivilegedUsersResponse { users: Vec::new(), }; @@ -305,7 +349,7 @@ pub struct RoomListResponse { } impl FromPacket for RoomListResponse { - fn from_packet(packet: &mut Packet) -> io::Result { + fn from_packet(packet: &mut Packet) -> result::Result { let mut response = RoomListResponse { rooms: Vec::new(), owned_private_rooms: Vec::new(), @@ -341,19 +385,23 @@ impl FromPacket for RoomListResponse { impl RoomListResponse { fn read_rooms(packet: &mut Packet, rooms: &mut Vec<(String, u32)>) - -> io::Result<()> + -> result::Result<()> { let original_rooms_len = rooms.len(); - let num_rooms = try!(packet.read_array(rooms, |packet| { - Ok((try!(packet.read_str()), 0)) - })); + let num_rooms_res: result::Result = + packet.read_array(rooms, |packet| { + Ok((try!(packet.read_str()), 0)) + }); + let num_rooms = try!(num_rooms_res); - let num_user_counts = try!(packet.read_array_with(|packet, i| { - let user_count = try!(packet.read_uint()); - rooms[original_rooms_len+i].1 = user_count; - Ok(()) - })); + let num_user_counts_res: result::Result = + packet.read_array_with(|packet, i| { + let user_count = try!(packet.read_uint()); + rooms[original_rooms_len+i].1 = user_count; + Ok(()) + }); + let num_user_counts = try!(num_user_counts_res); if num_rooms != num_user_counts { warn!("Numbers of rooms and user counts do not match: {} != {}", @@ -374,7 +422,7 @@ pub struct WishlistIntervalResponse { } impl FromPacket for WishlistIntervalResponse { - fn from_packet(packet: &mut Packet) -> io::Result { + fn from_packet(packet: &mut Packet) -> result::Result { let seconds = try!(packet.read_uint()); Ok(WishlistIntervalResponse { seconds: seconds, diff --git a/src/result.rs b/src/result.rs new file mode 100644 index 0000000..c5fc318 --- /dev/null +++ b/src/result.rs @@ -0,0 +1,121 @@ +use std::fmt; +use std::io; +use std::result; +use std::error; +use std::str; +use std::sync::mpsc; + +use rustc_serialize::json; +use websocket; + +use control; +use proto; + +#[derive(Debug)] +pub enum Error { + InvalidEnumError(usize), + IOError(io::Error), + JSONEncoderError(json::EncoderError), + JSONDecoderError(json::DecoderError), + SendControlRequestError(mpsc::SendError), + SendProtoResponseError(mpsc::SendError), + Utf8Error(str::Utf8Error), + WebSocketError(websocket::result::WebSocketError), +} + +impl fmt::Display for Error { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match *self { + Error::InvalidEnumError(n) => + write!( + fmt, "InvalidEnumError: {} is not a valid enum value", n + ), + Error::IOError(ref err) => + write!(fmt, "IOError: {}", err), + Error::JSONEncoderError(ref err) => + write!(fmt, "JSONEncoderError: {}", err), + Error::JSONDecoderError(ref err) => + write!(fmt, "JSONDecoderError: {}", err), + Error::SendControlRequestError(ref err) => + write!(fmt, "SendControlRequestError: {}", err), + Error::SendProtoResponseError(ref err) => + write!(fmt, "SendProtoResponseError: {}", err), + Error::Utf8Error(ref err) => + write!(fmt, "Utf8Error: {}", err), + Error::WebSocketError(ref err) => + write!(fmt, "WebSocketError: {}", err), + } + } +} + +impl error::Error for Error { + fn description(&self) -> &str { + match *self { + Error::InvalidEnumError(_) => "InvalidEnumError", + Error::IOError(_) => "IOError", + Error::JSONEncoderError(_) => "JSONEncoderError", + Error::JSONDecoderError(_) => "JSONDecoderError", + Error::SendControlRequestError(_) => "SendControlRequestError", + Error::SendProtoResponseError(_) => "SendProtoResponseError", + Error::Utf8Error(_) => "Utf8Error", + Error::WebSocketError(_) => "WebSocketError", + } + } + + fn cause(&self) -> Option<&error::Error> { + match *self { + Error::InvalidEnumError(_) => None, + Error::IOError(ref err) => Some(err), + Error::JSONEncoderError(ref err) => Some(err), + Error::JSONDecoderError(ref err) => Some(err), + Error::SendControlRequestError(ref err) => Some(err), + Error::SendProtoResponseError(ref err) => Some(err), + Error::Utf8Error(ref err) => Some(err), + Error::WebSocketError(ref err) => Some(err), + } + } +} + +impl From for Error { + fn from(err: io::Error) -> Self { + Error::IOError(err) + } +} + +impl From for Error { + fn from(err: json::EncoderError) -> Self { + Error::JSONEncoderError(err) + } +} + +impl From for Error { + fn from(err: json::DecoderError) -> Self { + Error::JSONDecoderError(err) + } +} + +impl From> for Error { + fn from(err: mpsc::SendError) -> Self { + Error::SendControlRequestError(err) + } +} + +impl From> for Error { + fn from(err: mpsc::SendError) -> Self { + Error::SendProtoResponseError(err) + } +} + +impl From for Error { + fn from(err: str::Utf8Error) -> Self { + Error::Utf8Error(err) + } +} + +impl From for Error { + fn from(err: websocket::result::WebSocketError) -> Self { + Error::WebSocketError(err) + } +} + +pub type Result = result::Result; diff --git a/src/user.rs b/src/user.rs index d3c475b..4d5ab4d 100644 --- a/src/user.rs +++ b/src/user.rs @@ -1,15 +1,40 @@ use std::collections; + use proto::server; +use result; + +const STATUS_OFFLINE: u32 = 1; +const STATUS_AWAY: u32 = 2; +const STATUS_ONLINE: u32 = 3; /// This enumeration is the list of possible user statuses. #[derive(Clone, Copy, Debug)] pub enum Status { /// The user if offline. - Offline = 1, + Offline, /// The user is connected, but AFK. - Away = 2, + Away, /// The user is present. - Online = 3, + Online, +} + +impl Status { + pub fn from_u32(n: u32) -> result::Result { + match n { + STATUS_OFFLINE => Ok(Status::Offline), + STATUS_AWAY => Ok(Status::Away), + STATUS_ONLINE => Ok(Status::Online), + _ => Err(result::Error::InvalidEnumError(n as usize)) + } + } + + pub fn to_u32(&self) -> u32 { + match *self { + Status::Offline => STATUS_OFFLINE, + Status::Away => STATUS_AWAY, + Status::Online => STATUS_ONLINE, + } + } } /// This structure contains the last known information about a fellow user. @@ -21,8 +46,10 @@ pub struct User { pub status: Status, /// The average upload speed of the user. pub average_speed: usize, - /// ??? + /// ??? Nicotine calls it downloadnum. pub num_downloads: usize, + /// ??? Unknown field. + pub unknown: usize, /// The number of files this user shares. pub num_files: usize, /// The number of folders this user shares.