feat(lib): chat-room protocol (list, join, leave, say) with events
Implement the Soulseek chat-room server messages against soulfind's wire format: - MessageFactory builders: room-list request (64), join (14), leave (15), say (13). - Handlers parsing the server side: RoomList (64) names+counts, JoinRoom (14) membership, SayChatroom (13), UserJoinedRoom (16), UserLeftRoom (17), LeaveRoom (15). - Surface everything to the client as a drained RoomEvent stream plus a room-list snapshot; new Client API: request_room_list, join_room, leave_room, say_in_room, room_list, take_room_events. Unit tests cover each builder and handler; server-gated e2e tests exercise message delivery and room listing against soulfind.
This commit is contained in:
@@ -122,6 +122,38 @@ impl MessageFactory {
|
||||
.clone()
|
||||
}
|
||||
|
||||
/// Ask the server (code 64) for the list of public chat rooms.
|
||||
#[must_use]
|
||||
pub fn build_room_list_request() -> Message {
|
||||
Message::new().write_int32(64).clone()
|
||||
}
|
||||
|
||||
/// Join a chat room (server code 14). `private` requests a private room.
|
||||
#[must_use]
|
||||
pub fn build_join_room(room: &str, private: bool) -> Message {
|
||||
Message::new()
|
||||
.write_int32(14)
|
||||
.write_string(room)
|
||||
.write_int32(u32::from(private))
|
||||
.clone()
|
||||
}
|
||||
|
||||
/// Leave a chat room (server code 15).
|
||||
#[must_use]
|
||||
pub fn build_leave_room(room: &str) -> Message {
|
||||
Message::new().write_int32(15).write_string(room).clone()
|
||||
}
|
||||
|
||||
/// Say `message` in chat room `room` (server code 13).
|
||||
#[must_use]
|
||||
pub fn build_say_chatroom(room: &str, message: &str) -> Message {
|
||||
Message::new()
|
||||
.write_int32(13)
|
||||
.write_string(room)
|
||||
.write_string(message)
|
||||
.clone()
|
||||
}
|
||||
|
||||
/// Ask a peer for their shared-file listing (peer code 4, no body).
|
||||
#[must_use]
|
||||
pub fn build_get_share_file_list() -> Message {
|
||||
@@ -272,6 +304,47 @@ fn test_build_message_acked() {
|
||||
assert_eq!(expect, message.get_data());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_join_room() {
|
||||
let message = MessageFactory::build_join_room("nicotine", false);
|
||||
let expect: Vec<u8> = [
|
||||
14, 0, 0, 0, // code
|
||||
8, 0, 0, 0, 110, 105, 99, 111, 116, 105, 110, 101, // "nicotine"
|
||||
0, 0, 0, 0, // private = 0
|
||||
]
|
||||
.to_vec();
|
||||
assert_eq!(expect, message.get_data());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_leave_room() {
|
||||
let message = MessageFactory::build_leave_room("nicotine");
|
||||
let expect: Vec<u8> = [
|
||||
15, 0, 0, 0, // code
|
||||
8, 0, 0, 0, 110, 105, 99, 111, 116, 105, 110, 101, // "nicotine"
|
||||
]
|
||||
.to_vec();
|
||||
assert_eq!(expect, message.get_data());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_say_chatroom() {
|
||||
let message = MessageFactory::build_say_chatroom("room", "hi");
|
||||
let expect: Vec<u8> = [
|
||||
13, 0, 0, 0, // code
|
||||
4, 0, 0, 0, 114, 111, 111, 109, // "room"
|
||||
2, 0, 0, 0, 104, 105, // "hi"
|
||||
]
|
||||
.to_vec();
|
||||
assert_eq!(expect, message.get_data());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_room_list_request() {
|
||||
let message = MessageFactory::build_room_list_request();
|
||||
assert_eq!(vec![64, 0, 0, 0], message.get_data());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_file_search_message() {
|
||||
let message = MessageFactory::build_file_search_message(12, "trance wax");
|
||||
|
||||
Reference in New Issue
Block a user