//! `GetUserStatus` (code 7) and `GetUserStats` (code 36): what the server //! knows about another user. //! //! The two answers arrive as separate messages, so a caller that wants a full //! picture asks for both and merges them — see `Client::request_user_info`. use crate::actor::server_actor::ServerMessage; use crate::message::{Message, MessageHandler}; use std::sync::mpsc::Sender; /// Receives `GetUserStatus` (code 7): `username`, `status`, `privileged`. pub struct GetUserStatusHandler; impl MessageHandler for GetUserStatusHandler { fn get_code(&self) -> u8 { 7 } fn handle(&self, message: &mut Message, sender: Sender) { let username = message.read_string(); let status = message.read_int32(); let privileged = message.read_bool(); let _ = sender.send(ServerMessage::UserStatusReceived { username, status, privileged, }); } } /// Receives `GetUserStats` (code 36): `username`, `average upload speed`, two /// obsolete fields, then the shared file and folder counts. pub struct GetUserStatsHandler; impl MessageHandler for GetUserStatsHandler { fn get_code(&self) -> u8 { 36 } fn handle(&self, message: &mut Message, sender: Sender) { let username = message.read_string(); let average_speed = message.read_int32(); let _upload_number = message.read_int32(); // obsolete, always 0 let _unknown = message.read_int32(); // obsolete, always 0 let shared_files = message.read_int32(); let shared_folders = message.read_int32(); let _ = sender.send(ServerMessage::UserStatsReceived { username, average_speed, shared_files, shared_folders, }); } } #[cfg(test)] mod tests { use super::*; /// Build a payload the dispatcher would hand a handler: 8 bytes of frame /// header, then the fields, with the pointer parked at the payload. fn payload(build: impl FnOnce(&mut Message)) -> Message { let mut message = Message::new(); message.write_raw_bytes(vec![0u8; 8]); build(&mut message); message.set_pointer(8); message } #[test] fn user_status_is_parsed_with_its_privileged_flag() { let (tx, rx) = std::sync::mpsc::channel(); let mut message = payload(|m| { m.write_string("alice").write_int32(2).write_int8(1); }); GetUserStatusHandler.handle(&mut message, tx); match rx.try_recv() { Ok(ServerMessage::UserStatusReceived { username, status, privileged, }) => { assert_eq!(username, "alice"); assert_eq!(status, 2); assert!(privileged); } other => panic!("unexpected: {other:?}"), } } #[test] fn user_stats_skip_the_two_obsolete_fields() { let (tx, rx) = std::sync::mpsc::channel(); // username, speed, obsolete, obsolete, files, folders let mut message = payload(|m| { m.write_string("bob") .write_int32(1024) .write_int32(0) .write_int32(0) .write_int32(4321) .write_int32(77); }); GetUserStatsHandler.handle(&mut message, tx); match rx.try_recv() { Ok(ServerMessage::UserStatsReceived { username, average_speed, shared_files, shared_folders, }) => { assert_eq!(username, "bob"); assert_eq!(average_speed, 1024); assert_eq!(shared_files, 4321, "must skip the obsolete pair"); assert_eq!(shared_folders, 77); } other => panic!("unexpected: {other:?}"), } } }