feat(lib): per-member room statistics from the JoinRoom reply

The JoinRoom (code 14) reply carries, after the membership list, four
parallel vectors describing each member: status, share statistics, free
upload slots and country. The handler stopped after the usernames, so all of
it was discarded. It is now parsed and exposed as Client::room_member_stats.

Issue #48 asked for RoomStats (57) and RoomListStats (55). Those codes are
UserInterests and MyRecommendations in the Soulseek protocol; there is no
RoomStats message on the wire. Per-room user counts already arrive with
RoomList (64), which the client handles, and the per-member statistics are
part of the JoinRoom reply -- so that is where this adds them.

- join_room.rs: parse the stat vectors, each bounded by the payload so a
  hostile count cannot hang or overallocate
- types::RoomUserStats; every field looked up positionally so a server
  sending short vectors (or omitting countries) does not shift the roster
- ServerMessage/ClientOperation::RoomMemberStats and the context snapshot
  behind Client::room_member_stats, replaced on each rejoin
This commit is contained in:
Michel de Graaf
2026-08-28 12:44:37 +00:00
parent 0f91ffdcf3
commit fa49ebd2eb
7 changed files with 391 additions and 16 deletions
+19
View File
@@ -328,6 +328,25 @@ impl UserInfo {
}
}
/// What the server reports about one member of a room we joined, from the
/// per-member stat vectors of a `JoinRoom` (code 14) reply.
///
/// A server may send shorter stat vectors than the membership list (older
/// servers omit country codes entirely), so absent fields read as zero and an
/// unreported country is `None` rather than an empty string.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RoomUserStats {
pub username: String,
pub status: UserStatus,
pub average_speed: u32,
pub shared_files: u32,
pub shared_folders: u32,
/// Upload slots the member has free; zero means they are busy.
pub slots_free: u32,
/// Two-letter country code, when the server reports one.
pub country: Option<String>,
}
/// A public chat room advertised by the server (`RoomList`, code 64).
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RoomInfo {