use std::net; use log::{debug, warn}; use crate::core::{ ValueDecode, ValueDecodeError, ValueDecoder, ValueEncode, ValueEncodeError, ValueEncoder, }; use crate::peer::PeerConnectionType; use crate::server::constants::*; use crate::{User, UserStatus}; /*=================* * SERVER RESPONSE * *=================*/ #[derive(Clone, Debug, Eq, PartialEq)] pub enum ServerResponse { ConnectToPeerResponse(ConnectToPeerResponse), FileSearchResponse(FileSearchResponse), LoginResponse(LoginResponse), PeerAddressResponse(PeerAddressResponse), PrivilegedUsersResponse(PrivilegedUsersResponse), RoomJoinResponse(RoomJoinResponse), RoomLeaveResponse(RoomLeaveResponse), RoomListResponse(RoomListResponse), RoomMessageResponse(RoomMessageResponse), RoomTickersResponse(RoomTickersResponse), RoomUserJoinedResponse(RoomUserJoinedResponse), RoomUserLeftResponse(RoomUserLeftResponse), UserInfoResponse(UserInfoResponse), UserStatusResponse(UserStatusResponse), WishlistIntervalResponse(WishlistIntervalResponse), // Unknown purpose ParentMinSpeedResponse(ParentMinSpeedResponse), ParentSpeedRatioResponse(ParentSpeedRatioResponse), UnknownResponse(u32), } impl ValueEncode for ServerResponse { fn encode_to( &self, encoder: &mut ValueEncoder, ) -> Result<(), ValueEncodeError> { match *self { ServerResponse::ConnectToPeerResponse(ref response) => { encoder.encode_u32(CODE_CONNECT_TO_PEER)?; response.encode_to(encoder)?; } ServerResponse::FileSearchResponse(ref response) => { encoder.encode_u32(CODE_FILE_SEARCH)?; response.encode_to(encoder)?; } ServerResponse::LoginResponse(ref response) => { encoder.encode_u32(CODE_LOGIN)?; response.encode_to(encoder)?; } ServerResponse::ParentMinSpeedResponse(ref response) => { encoder.encode_u32(CODE_PARENT_MIN_SPEED)?; response.encode_to(encoder)?; } ServerResponse::ParentSpeedRatioResponse(ref response) => { encoder.encode_u32(CODE_PARENT_SPEED_RATIO)?; response.encode_to(encoder)?; } ServerResponse::PeerAddressResponse(ref response) => { encoder.encode_u32(CODE_PEER_ADDRESS)?; response.encode_to(encoder)?; } ServerResponse::PrivilegedUsersResponse(ref response) => { encoder.encode_u32(CODE_PRIVILEGED_USERS)?; response.encode_to(encoder)?; } ServerResponse::RoomJoinResponse(ref response) => { encoder.encode_u32(CODE_ROOM_JOIN)?; response.encode_to(encoder)?; } ServerResponse::RoomLeaveResponse(ref response) => { encoder.encode_u32(CODE_ROOM_LEAVE)?; response.encode_to(encoder)?; } ServerResponse::RoomListResponse(ref response) => { encoder.encode_u32(CODE_ROOM_LIST)?; response.encode_to(encoder)?; } ServerResponse::RoomMessageResponse(ref response) => { encoder.encode_u32(CODE_ROOM_MESSAGE)?; response.encode_to(encoder)?; } ServerResponse::RoomTickersResponse(ref response) => { encoder.encode_u32(CODE_ROOM_TICKERS)?; response.encode_to(encoder)?; } ServerResponse::RoomUserJoinedResponse(ref response) => { encoder.encode_u32(CODE_ROOM_USER_JOINED)?; response.encode_to(encoder)?; } ServerResponse::RoomUserLeftResponse(ref response) => { encoder.encode_u32(CODE_ROOM_USER_LEFT)?; response.encode_to(encoder)?; } ServerResponse::UserInfoResponse(ref response) => { encoder.encode_u32(CODE_USER_INFO)?; response.encode_to(encoder)?; } ServerResponse::UserStatusResponse(ref response) => { encoder.encode_u32(CODE_USER_STATUS)?; response.encode_to(encoder)?; } ServerResponse::WishlistIntervalResponse(ref response) => { encoder.encode_u32(CODE_WISHLIST_INTERVAL)?; response.encode_to(encoder)?; } _ => { unimplemented!(); } }; Ok(()) } } impl ValueDecode for ServerResponse { fn decode_from(decoder: &mut ValueDecoder) -> Result { let position = decoder.position(); let code: u32 = decoder.decode()?; let response = match code { CODE_CONNECT_TO_PEER => { let response = decoder.decode()?; ServerResponse::ConnectToPeerResponse(response) } CODE_FILE_SEARCH => { let response = decoder.decode()?; ServerResponse::FileSearchResponse(response) } CODE_LOGIN => { let response = decoder.decode()?; ServerResponse::LoginResponse(response) } CODE_PARENT_MIN_SPEED => { let response = decoder.decode()?; ServerResponse::ParentMinSpeedResponse(response) } CODE_PARENT_SPEED_RATIO => { let response = decoder.decode()?; ServerResponse::ParentSpeedRatioResponse(response) } CODE_PEER_ADDRESS => { let response = decoder.decode()?; ServerResponse::PeerAddressResponse(response) } CODE_PRIVILEGED_USERS => { let response = decoder.decode()?; ServerResponse::PrivilegedUsersResponse(response) } CODE_ROOM_JOIN => { let response = decoder.decode()?; ServerResponse::RoomJoinResponse(response) } CODE_ROOM_LEAVE => { let response = decoder.decode()?; ServerResponse::RoomLeaveResponse(response) } CODE_ROOM_LIST => { let response = decoder.decode()?; ServerResponse::RoomListResponse(response) } CODE_ROOM_MESSAGE => { let response = decoder.decode()?; ServerResponse::RoomMessageResponse(response) } CODE_ROOM_TICKERS => { let response = decoder.decode()?; ServerResponse::RoomTickersResponse(response) } CODE_ROOM_USER_JOINED => { let response = decoder.decode()?; ServerResponse::RoomUserJoinedResponse(response) } CODE_ROOM_USER_LEFT => { let response = decoder.decode()?; ServerResponse::RoomUserLeftResponse(response) } CODE_USER_INFO => { let response = decoder.decode()?; ServerResponse::UserInfoResponse(response) } CODE_USER_STATUS => { let response = decoder.decode()?; ServerResponse::UserStatusResponse(response) } CODE_WISHLIST_INTERVAL => { let response = decoder.decode()?; ServerResponse::WishlistIntervalResponse(response) } _ => { return Err(ValueDecodeError::InvalidData { value_name: "server response code".to_string(), cause: format!("unknown value {}", code), position: position, }); } }; Ok(response) } } /*=================* * CONNECT TO PEER * *=================*/ #[derive(Clone, Debug, Eq, PartialEq)] pub struct ConnectToPeerResponse { pub user_name: String, pub connection_type: PeerConnectionType, pub ip: net::Ipv4Addr, pub port: u16, pub token: u32, pub is_privileged: bool, } impl From for ServerResponse { fn from(response: ConnectToPeerResponse) -> Self { Self::ConnectToPeerResponse(response) } } impl ValueEncode for ConnectToPeerResponse { fn encode_to( &self, encoder: &mut ValueEncoder, ) -> Result<(), ValueEncodeError> { encoder.encode(&self.user_name)?; encoder.encode(&self.connection_type)?; encoder.encode(&self.ip)?; encoder.encode_u16(self.port)?; encoder.encode_u32(self.token)?; encoder.encode_bool(self.is_privileged) } } impl ValueDecode for ConnectToPeerResponse { fn decode_from(decoder: &mut ValueDecoder) -> Result { let user_name = decoder.decode()?; let connection_type = decoder.decode()?; let ip = decoder.decode()?; let port = decoder.decode()?; let token = decoder.decode()?; let is_privileged = decoder.decode()?; Ok(ConnectToPeerResponse { user_name, connection_type, ip, port, token, is_privileged, }) } } /*=============* * FILE SEARCH * *=============*/ #[derive(Clone, Debug, Eq, PartialEq)] pub struct FileSearchResponse { pub user_name: String, pub ticket: u32, pub query: String, } impl From for ServerResponse { fn from(response: FileSearchResponse) -> Self { Self::FileSearchResponse(response) } } impl ValueEncode for FileSearchResponse { fn encode_to( &self, encoder: &mut ValueEncoder, ) -> Result<(), ValueEncodeError> { encoder.encode_string(&self.user_name)?; encoder.encode_u32(self.ticket)?; encoder.encode_string(&self.query) } } impl ValueDecode for FileSearchResponse { fn decode_from(decoder: &mut ValueDecoder) -> Result { let user_name = decoder.decode()?; let ticket = decoder.decode()?; let query = decoder.decode()?; Ok(FileSearchResponse { user_name, ticket, query, }) } } /*=======* * LOGIN * *=======*/ #[derive(Clone, Debug, Eq, PartialEq)] pub enum LoginResponse { LoginOk { motd: String, ip: net::Ipv4Addr, password_md5_opt: Option, }, LoginFail { reason: String, }, } impl From for ServerResponse { fn from(response: LoginResponse) -> Self { Self::LoginResponse(response) } } impl ValueEncode for LoginResponse { fn encode_to( &self, encoder: &mut ValueEncoder, ) -> Result<(), ValueEncodeError> { match *self { LoginResponse::LoginOk { ref motd, ip, password_md5_opt: _, } => { encoder.encode_bool(true)?; encoder.encode(motd)?; encoder.encode(&ip)?; } LoginResponse::LoginFail { ref reason } => { encoder.encode_bool(false)?; encoder.encode(reason)?; } }; Ok(()) } } impl ValueDecode for LoginResponse { fn decode_from(decoder: &mut ValueDecoder) -> Result { let ok: bool = decoder.decode()?; if !ok { let reason = decoder.decode()?; return Ok(LoginResponse::LoginFail { reason }); } let motd = decoder.decode()?; let ip = decoder.decode()?; let result = decoder.decode::(); match result { Ok(value) => debug!("LoginResponse last field: {}", value), Err(e) => debug!("Error reading LoginResponse field: {:?}", e), } Ok(LoginResponse::LoginOk { motd, ip, password_md5_opt: None, }) } } /*==================* * PARENT MIN SPEED * *==================*/ #[derive(Clone, Debug, Eq, PartialEq)] pub struct ParentMinSpeedResponse { pub value: u32, } impl From for ServerResponse { fn from(response: ParentMinSpeedResponse) -> Self { Self::ParentMinSpeedResponse(response) } } impl ValueEncode for ParentMinSpeedResponse { fn encode_to( &self, encoder: &mut ValueEncoder, ) -> Result<(), ValueEncodeError> { encoder.encode_u32(self.value) } } impl ValueDecode for ParentMinSpeedResponse { fn decode_from(decoder: &mut ValueDecoder) -> Result { let value = decoder.decode()?; Ok(ParentMinSpeedResponse { value }) } } /*====================* * PARENT SPEED RATIO * *====================*/ #[derive(Clone, Debug, Eq, PartialEq)] pub struct ParentSpeedRatioResponse { pub value: u32, } impl From for ServerResponse { fn from(response: ParentSpeedRatioResponse) -> Self { Self::ParentSpeedRatioResponse(response) } } impl ValueEncode for ParentSpeedRatioResponse { fn encode_to( &self, encoder: &mut ValueEncoder, ) -> Result<(), ValueEncodeError> { encoder.encode_u32(self.value) } } impl ValueDecode for ParentSpeedRatioResponse { fn decode_from(decoder: &mut ValueDecoder) -> Result { let value = decoder.decode()?; Ok(ParentSpeedRatioResponse { value }) } } /*==============* * PEER ADDRESS * *==============*/ #[derive(Clone, Debug, Eq, PartialEq)] pub struct PeerAddressResponse { user_name: String, ip: net::Ipv4Addr, port: u16, } impl From for ServerResponse { fn from(response: PeerAddressResponse) -> Self { Self::PeerAddressResponse(response) } } impl ValueEncode for PeerAddressResponse { fn encode_to( &self, encoder: &mut ValueEncoder, ) -> Result<(), ValueEncodeError> { encoder.encode(&self.user_name)?; encoder.encode(&self.ip)?; encoder.encode_u16(self.port) } } impl ValueDecode for PeerAddressResponse { fn decode_from(decoder: &mut ValueDecoder) -> Result { let user_name = decoder.decode()?; let ip = decoder.decode()?; let port = decoder.decode()?; Ok(PeerAddressResponse { user_name, ip, port, }) } } /*==================* * PRIVILEGED USERS * *==================*/ #[derive(Clone, Debug, Eq, PartialEq)] pub struct PrivilegedUsersResponse { pub users: Vec, } impl From for ServerResponse { fn from(response: PrivilegedUsersResponse) -> Self { Self::PrivilegedUsersResponse(response) } } impl ValueEncode for PrivilegedUsersResponse { fn encode_to( &self, encoder: &mut ValueEncoder, ) -> Result<(), ValueEncodeError> { encoder.encode(&self.users) } } impl ValueDecode for PrivilegedUsersResponse { fn decode_from(decoder: &mut ValueDecoder) -> Result { let users = decoder.decode()?; Ok(PrivilegedUsersResponse { users }) } } /*===========* * ROOM JOIN * *===========*/ #[derive(Clone, Debug, Eq, PartialEq)] pub struct RoomJoinResponse { pub room_name: String, pub users: Vec, pub owner: Option, pub operators: Vec, } impl From for ServerResponse { fn from(response: RoomJoinResponse) -> Self { Self::RoomJoinResponse(response) } } // This struct is defined to enable decoding a vector of such values for // `RoomJoinResponse`, but its data is inlined in the `User` struct. // For details about individual fields, see said `User` struct. #[derive(Clone, Debug, Eq, PartialEq)] struct UserInfo { average_speed: u32, num_downloads: u32, unknown: u32, num_files: u32, num_folders: u32, } impl UserInfo { fn from_user(user: &User) -> Self { Self { average_speed: user.average_speed as u32, num_downloads: user.num_downloads as u32, unknown: user.unknown as u32, num_files: user.num_files as u32, num_folders: user.num_folders as u32, } } } fn build_user( name: String, status: UserStatus, info: UserInfo, num_free_slots: u32, country: String, ) -> User { User { name, status, average_speed: info.average_speed as usize, num_downloads: info.num_downloads as usize, unknown: info.unknown as usize, num_files: info.num_files as usize, num_folders: info.num_folders as usize, num_free_slots: num_free_slots as usize, country, } } impl ValueEncode for UserInfo { fn encode_to( &self, encoder: &mut ValueEncoder, ) -> Result<(), ValueEncodeError> { encoder.encode_u32(self.average_speed)?; encoder.encode_u32(self.num_downloads)?; encoder.encode_u32(self.unknown)?; encoder.encode_u32(self.num_files)?; encoder.encode_u32(self.num_folders) } } impl ValueDecode for UserInfo { fn decode_from(decoder: &mut ValueDecoder) -> Result { let average_speed = decoder.decode()?; let num_downloads = decoder.decode()?; let unknown = decoder.decode()?; let num_files = decoder.decode()?; let num_folders = decoder.decode()?; Ok(UserInfo { average_speed, num_downloads, unknown, num_files, num_folders, }) } } impl ValueEncode for RoomJoinResponse { fn encode_to( &self, encoder: &mut ValueEncoder, ) -> Result<(), ValueEncodeError> { let mut user_names = vec![]; let mut user_statuses = vec![]; let mut user_infos = vec![]; let mut user_free_slots = vec![]; let mut user_countries = vec![]; for user in &self.users { user_names.push(&user.name); user_statuses.push(user.status); user_infos.push(UserInfo::from_user(user)); user_free_slots.push(user.num_free_slots as u32); user_countries.push(&user.country); } encoder.encode_string(&self.room_name)?; encoder.encode(&user_names)?; encoder.encode(&user_statuses)?; encoder.encode(&user_infos)?; encoder.encode(&user_free_slots)?; encoder.encode(&user_countries)?; if let Some(ref owner) = self.owner { encoder.encode_string(owner)?; encoder.encode(&self.operators)?; } Ok(()) } } fn build_users( mut names: Vec, mut statuses: Vec, mut infos: Vec, mut free_slots: Vec, mut countries: Vec, ) -> Vec { let mut users = vec![]; loop { let name_opt = names.pop(); let status_opt = statuses.pop(); let info_opt = infos.pop(); let slots_opt = free_slots.pop(); let country_opt = countries.pop(); match (name_opt, status_opt, info_opt, slots_opt, country_opt) { (Some(name), Some(status), Some(info), Some(slots), Some(country)) => { users.push(build_user(name, status, info, slots, country)) } _ => break, } } users.reverse(); users } impl ValueDecode for RoomJoinResponse { fn decode_from(decoder: &mut ValueDecoder) -> Result { let room_name = decoder.decode()?; let user_names = decoder.decode()?; let user_statuses = decoder.decode()?; let user_infos = decoder.decode()?; let user_free_slots = decoder.decode()?; let user_countries = decoder.decode()?; let mut owner = None; let mut operators = vec![]; if decoder.has_remaining() { owner = Some(decoder.decode()?); operators = decoder.decode()?; } let users = build_users( user_names, user_statuses, user_infos, user_free_slots, user_countries, ); Ok(RoomJoinResponse { room_name, users, owner, operators, }) } } /*============* * ROOM LEAVE * *============*/ #[derive(Clone, Debug, Eq, PartialEq)] pub struct RoomLeaveResponse { pub room_name: String, } impl From for ServerResponse { fn from(response: RoomLeaveResponse) -> Self { Self::RoomLeaveResponse(response) } } impl ValueEncode for RoomLeaveResponse { fn encode_to( &self, encoder: &mut ValueEncoder, ) -> Result<(), ValueEncodeError> { encoder.encode_string(&self.room_name) } } impl ValueDecode for RoomLeaveResponse { fn decode_from(decoder: &mut ValueDecoder) -> Result { let room_name = decoder.decode()?; Ok(RoomLeaveResponse { room_name }) } } /*===========* * ROOM LIST * *===========*/ #[derive(Clone, Debug, Eq, PartialEq)] pub struct RoomListResponse { pub rooms: Vec<(String, u32)>, pub owned_private_rooms: Vec<(String, u32)>, pub other_private_rooms: Vec<(String, u32)>, pub operated_private_room_names: Vec, } impl From for ServerResponse { fn from(response: RoomListResponse) -> Self { Self::RoomListResponse(response) } } impl RoomListResponse { fn build_rooms( mut room_names: Vec, mut user_counts: Vec, ) -> Vec<(String, u32)> { let mut rooms = vec![]; loop { let room_name_opt = room_names.pop(); let user_count_opt = user_counts.pop(); match (room_name_opt, user_count_opt) { (Some(room_name), Some(user_count)) => { rooms.push((room_name, user_count)) } _ => break, } } if !room_names.is_empty() { warn!( "Unmatched room names in room list response: {:?}", room_names ) } if !user_counts.is_empty() { warn!( "Unmatched user counts in room list response: {:?}", user_counts ) } rooms.reverse(); rooms } fn decode_rooms( decoder: &mut ValueDecoder, ) -> Result, ValueDecodeError> { let room_names = decoder.decode()?; let user_counts = decoder.decode()?; Ok(Self::build_rooms(room_names, user_counts)) } fn encode_rooms( rooms: &[(String, u32)], encoder: &mut ValueEncoder, ) -> Result<(), ValueEncodeError> { let mut room_names = vec![]; let mut user_counts = vec![]; for &(ref room_name, user_count) in rooms { room_names.push(room_name); user_counts.push(user_count); } encoder.encode(&room_names)?; encoder.encode(&user_counts) } } impl ValueEncode for RoomListResponse { fn encode_to( &self, encoder: &mut ValueEncoder, ) -> Result<(), ValueEncodeError> { Self::encode_rooms(&self.rooms, encoder)?; Self::encode_rooms(&self.owned_private_rooms, encoder)?; Self::encode_rooms(&self.other_private_rooms, encoder)?; encoder.encode(&self.operated_private_room_names) } } impl ValueDecode for RoomListResponse { fn decode_from(decoder: &mut ValueDecoder) -> Result { let rooms = RoomListResponse::decode_rooms(decoder)?; let owned_private_rooms = RoomListResponse::decode_rooms(decoder)?; let other_private_rooms = RoomListResponse::decode_rooms(decoder)?; let operated_private_room_names = decoder.decode()?; Ok(RoomListResponse { rooms, owned_private_rooms, other_private_rooms, operated_private_room_names, }) } } /*==============* * ROOM MESSAGE * *==============*/ #[derive(Clone, Debug, Eq, PartialEq)] pub struct RoomMessageResponse { pub room_name: String, pub user_name: String, pub message: String, } impl From for ServerResponse { fn from(response: RoomMessageResponse) -> Self { Self::RoomMessageResponse(response) } } impl ValueEncode for RoomMessageResponse { fn encode_to( &self, encoder: &mut ValueEncoder, ) -> Result<(), ValueEncodeError> { encoder.encode_string(&self.room_name)?; encoder.encode_string(&self.user_name)?; encoder.encode_string(&self.message) } } impl ValueDecode for RoomMessageResponse { fn decode_from(decoder: &mut ValueDecoder) -> Result { let room_name = decoder.decode()?; let user_name = decoder.decode()?; let message = decoder.decode()?; Ok(RoomMessageResponse { room_name, user_name, message, }) } } /*==============* * ROOM TICKERS * *==============*/ #[derive(Clone, Debug, Eq, PartialEq)] pub struct RoomTickersResponse { pub room_name: String, pub tickers: Vec<(String, String)>, } impl From for ServerResponse { fn from(response: RoomTickersResponse) -> Self { Self::RoomTickersResponse(response) } } impl ValueEncode for RoomTickersResponse { fn encode_to( &self, encoder: &mut ValueEncoder, ) -> Result<(), ValueEncodeError> { encoder.encode_string(&self.room_name)?; encoder.encode(&self.tickers) } } impl ValueDecode for RoomTickersResponse { fn decode_from(decoder: &mut ValueDecoder) -> Result { let room_name = decoder.decode()?; let tickers = decoder.decode()?; Ok(RoomTickersResponse { room_name, tickers }) } } /*==================* * ROOM USER JOINED * *==================*/ #[derive(Clone, Debug, Eq, PartialEq)] pub struct RoomUserJoinedResponse { pub room_name: String, pub user: User, } impl From for ServerResponse { fn from(response: RoomUserJoinedResponse) -> Self { Self::RoomUserJoinedResponse(response) } } impl ValueEncode for RoomUserJoinedResponse { fn encode_to( &self, encoder: &mut ValueEncoder, ) -> Result<(), ValueEncodeError> { encoder.encode_string(&self.room_name)?; encoder.encode_string(&self.user.name)?; self.user.status.encode_to(encoder)?; UserInfo::from_user(&self.user).encode_to(encoder)?; encoder.encode_u32(self.user.num_free_slots as u32)?; encoder.encode_string(&self.user.country) } } impl ValueDecode for RoomUserJoinedResponse { fn decode_from(decoder: &mut ValueDecoder) -> Result { let room_name = decoder.decode()?; let user_name = decoder.decode()?; let status = decoder.decode()?; let info = decoder.decode()?; let num_free_slots = decoder.decode()?; let country = decoder.decode()?; Ok(RoomUserJoinedResponse { room_name, user: build_user(user_name, status, info, num_free_slots, country), }) } } /*================* * ROOM USER LEFT * *================*/ #[derive(Clone, Debug, Eq, PartialEq)] pub struct RoomUserLeftResponse { pub room_name: String, pub user_name: String, } impl From for ServerResponse { fn from(response: RoomUserLeftResponse) -> Self { Self::RoomUserLeftResponse(response) } } impl ValueEncode for RoomUserLeftResponse { fn encode_to( &self, encoder: &mut ValueEncoder, ) -> Result<(), ValueEncodeError> { encoder.encode_string(&self.room_name)?; encoder.encode_string(&self.user_name) } } impl ValueDecode for RoomUserLeftResponse { fn decode_from(decoder: &mut ValueDecoder) -> Result { let room_name = decoder.decode()?; let user_name = decoder.decode()?; Ok(RoomUserLeftResponse { room_name, user_name, }) } } /*===========* * USER INFO * *===========*/ #[derive(Clone, Debug, Eq, PartialEq)] pub struct UserInfoResponse { pub user_name: String, pub average_speed: usize, pub num_downloads: usize, pub num_files: usize, pub num_folders: usize, } impl From for ServerResponse { fn from(response: UserInfoResponse) -> Self { Self::UserInfoResponse(response) } } impl ValueEncode for UserInfoResponse { fn encode_to( &self, encoder: &mut ValueEncoder, ) -> Result<(), ValueEncodeError> { encoder.encode_string(&self.user_name)?; encoder.encode_u32(self.average_speed as u32)?; encoder.encode_u32(self.num_downloads as u32)?; encoder.encode_u32(self.num_files as u32)?; encoder.encode_u32(self.num_folders as u32) } } impl ValueDecode for UserInfoResponse { fn decode_from(decoder: &mut ValueDecoder) -> Result { let user_name = decoder.decode()?; let average_speed: u32 = decoder.decode()?; let num_downloads: u32 = decoder.decode()?; let num_files: u32 = decoder.decode()?; let num_folders: u32 = decoder.decode()?; Ok(UserInfoResponse { user_name, average_speed: average_speed as usize, num_downloads: num_downloads as usize, num_files: num_files as usize, num_folders: num_folders as usize, }) } } /*=============* * USER STATUS * *=============*/ #[derive(Clone, Debug, Eq, PartialEq)] pub struct UserStatusResponse { pub user_name: String, pub status: UserStatus, pub is_privileged: bool, } impl From for ServerResponse { fn from(response: UserStatusResponse) -> Self { Self::UserStatusResponse(response) } } impl ValueEncode for UserStatusResponse { fn encode_to( &self, encoder: &mut ValueEncoder, ) -> Result<(), ValueEncodeError> { encoder.encode_string(&self.user_name)?; self.status.encode_to(encoder)?; encoder.encode_bool(self.is_privileged) } } impl ValueDecode for UserStatusResponse { fn decode_from(decoder: &mut ValueDecoder) -> Result { let user_name = decoder.decode()?; let status = decoder.decode()?; let is_privileged = decoder.decode()?; Ok(UserStatusResponse { user_name, status, is_privileged, }) } } /*===================* * WISHLIST INTERVAL * *===================*/ #[derive(Clone, Debug, Eq, PartialEq)] pub struct WishlistIntervalResponse { pub seconds: u32, } impl From for ServerResponse { fn from(response: WishlistIntervalResponse) -> Self { Self::WishlistIntervalResponse(response) } } impl ValueEncode for WishlistIntervalResponse { fn encode_to( &self, encoder: &mut ValueEncoder, ) -> Result<(), ValueEncodeError> { encoder.encode_u32(self.seconds) } } impl ValueDecode for WishlistIntervalResponse { fn decode_from(decoder: &mut ValueDecoder) -> Result { let seconds = decoder.decode()?; Ok(WishlistIntervalResponse { seconds }) } } /*=======* * TESTS * *=======*/ #[cfg(test)] mod tests { use std::net; use bytes::BytesMut; use crate::core::testing::roundtrip; use crate::core::{ValueDecodeError, ValueDecoder}; use super::*; #[test] fn invalid_code() { let mut bytes = BytesMut::new(); bytes.extend_from_slice(&[57, 5, 0, 0]); let result = ValueDecoder::new(&bytes).decode::(); assert_eq!( result, Err(ValueDecodeError::InvalidData { value_name: "server response code".to_string(), cause: "unknown value 1337".to_string(), position: 0, }) ); } #[test] fn roundtrip_connect_to_peer() { roundtrip(ServerResponse::ConnectToPeerResponse( ConnectToPeerResponse { user_name: "alice".to_string(), connection_type: PeerConnectionType::Peer, ip: net::Ipv4Addr::new(192, 168, 254, 1), port: 1337, token: 42, is_privileged: true, }, )) } #[test] fn roundtrip_file_search() { roundtrip(ServerResponse::FileSearchResponse(FileSearchResponse { user_name: "alice".to_string(), ticket: 1337, query: "foo.txt".to_string(), })) } #[test] fn roundtrip_login_ok() { roundtrip(ServerResponse::LoginResponse(LoginResponse::LoginOk { motd: "welcome one welcome all!".to_string(), ip: net::Ipv4Addr::new(127, 0, 0, 1), password_md5_opt: None, })) } #[test] fn roundtrip_login_fail() { roundtrip(ServerResponse::LoginResponse(LoginResponse::LoginFail { reason: "I just don't like you".to_string(), })) } #[test] fn roundtrip_parent_min_speed() { roundtrip(ServerResponse::ParentMinSpeedResponse( ParentMinSpeedResponse { value: 1337 }, )) } #[test] fn roundtrip_parent_speed_ratio() { roundtrip(ServerResponse::ParentSpeedRatioResponse( ParentSpeedRatioResponse { value: 1337 }, )) } #[test] fn roundtrip_peer_address() { roundtrip(ServerResponse::PeerAddressResponse(PeerAddressResponse { user_name: "alice".to_string(), ip: net::Ipv4Addr::new(127, 0, 0, 1), port: 1337, })) } #[test] fn roundtrip_privileged_users() { roundtrip(ServerResponse::PrivilegedUsersResponse( PrivilegedUsersResponse { users: vec![ "alice".to_string(), "bob".to_string(), "chris".to_string(), "dory".to_string(), ], }, )) } #[test] fn roundtrip_room_join() { roundtrip(ServerResponse::RoomJoinResponse(RoomJoinResponse { room_name: "red".to_string(), users: vec![ User { name: "alice".to_string(), status: UserStatus::Online, average_speed: 1000, num_downloads: 1001, unknown: 1002, num_files: 1003, num_folders: 1004, num_free_slots: 1005, country: "US".to_string(), }, User { name: "barbara".to_string(), status: UserStatus::Away, average_speed: 2000, num_downloads: 2001, unknown: 2002, num_files: 2003, num_folders: 2004, num_free_slots: 2005, country: "DE".to_string(), }, ], owner: Some("carol".to_string()), operators: vec!["deirdre".to_string(), "erica".to_string()], })) } #[test] fn roundtrip_room_join_no_owner() { roundtrip(ServerResponse::RoomJoinResponse(RoomJoinResponse { room_name: "red".to_string(), users: vec![], owner: None, operators: vec![], })) } #[test] fn roundtrip_room_leave() { roundtrip(ServerResponse::RoomLeaveResponse(RoomLeaveResponse { room_name: "red".to_string(), })) } #[test] fn roundtrip_room_list() { roundtrip(ServerResponse::RoomListResponse(RoomListResponse { rooms: vec![("red".to_string(), 12), ("blue".to_string(), 13)], owned_private_rooms: vec![ ("green".to_string(), 14), ("purple".to_string(), 15), ], other_private_rooms: vec![ ("yellow".to_string(), 16), ("orange".to_string(), 17), ], operated_private_room_names: vec![ "brown".to_string(), "pink".to_string(), ], })) } #[test] fn roundtrip_room_message() { roundtrip(ServerResponse::RoomMessageResponse(RoomMessageResponse { room_name: "red".to_string(), user_name: "alice".to_string(), message: "hello world!".to_string(), })) } #[test] fn roundtrip_room_tickers() { roundtrip(ServerResponse::RoomTickersResponse(RoomTickersResponse { room_name: "red".to_string(), tickers: vec![ ("alice".to_string(), "hello world!".to_string()), ("bob".to_string(), "hi alice :)".to_string()), ], })) } #[test] fn roundtrip_room_user_joined() { roundtrip(ServerResponse::RoomUserJoinedResponse( RoomUserJoinedResponse { room_name: "red".to_string(), user: User { name: "alice".to_string(), status: UserStatus::Online, average_speed: 1000, num_downloads: 1001, unknown: 1002, num_files: 1003, num_folders: 1004, num_free_slots: 1005, country: "AR".to_string(), }, }, )) } #[test] fn roundtrip_room_user_left() { roundtrip(ServerResponse::RoomUserLeftResponse(RoomUserLeftResponse { room_name: "red".to_string(), user_name: "alice".to_string(), })) } #[test] fn roundtrip_user_info() { roundtrip(ServerResponse::UserInfoResponse(UserInfoResponse { user_name: "alice".to_string(), average_speed: 1000, num_downloads: 1001, num_files: 1002, num_folders: 1003, })) } #[test] fn roundtrip_user_status() { roundtrip(ServerResponse::UserStatusResponse(UserStatusResponse { user_name: "alice".to_string(), status: UserStatus::Offline, is_privileged: true, })) } #[test] fn roundtrip_wishlist_interval() { roundtrip(ServerResponse::WishlistIntervalResponse( WishlistIntervalResponse { seconds: 1337 }, )) } }