runecast_protocol/player.rs
1//! Player identity and context types.
2//!
3//! These types are the single source of truth for player identity information,
4//! shared across the handlers crate and backend.
5
6use crate::ServerMessage;
7
8/// Core player identity - the immutable parts that identify a player.
9///
10/// This is embedded in all player-related structs to avoid field duplication.
11/// Contains the fields that are constant for a player's session.
12pub trait PlayerIdentity {
13 /// Get player ID.
14 fn player_id(&self) -> i64;
15
16 /// Get the raw Discord username (the unique handle). For display, use `name()`.
17 fn username(&self) -> &str;
18
19 /// Get avatar URL.
20 fn avatar_url(&self) -> &str;
21
22 /// What to call this player in their current context, if the server
23 /// resolved it. `None` means "show `username`".
24 fn name(&self) -> Option<&crate::PlayerName> {
25 None
26 }
27}
28
29#[async_trait::async_trait]
30pub trait PlayerContext: Send + Sync {
31 async fn send_message(&self, msg: ServerMessage);
32
33 /// Get the player's identity.
34 fn identity(&self) -> &dyn PlayerIdentity;
35
36 /// Check if the player has admin privileges.
37 fn is_admin(&self) -> bool;
38
39 /// Get the current lobby ID if in a lobby.
40 fn lobby_id(&self) -> Option<&str>;
41
42 /// Get the current game ID if in a game.
43 fn game_id(&self) -> Option<&str>;
44
45 /// Check if the player is spectating.
46 fn is_spectating(&self) -> bool;
47
48 /// Check if the player is connected.
49 fn is_connected(&self) -> bool;
50
51 // =========================================================================
52 // Convenience methods with default implementations
53 // =========================================================================
54
55 /// Get player ID directly (convenience for `identity().player_id()`).
56 fn player_id(&self) -> i64 {
57 self.identity().player_id()
58 }
59
60 /// Check if the player is in a lobby.
61 fn in_lobby(&self) -> bool {
62 self.lobby_id().is_some()
63 }
64
65 /// Check if the player is in a game.
66 fn in_game(&self) -> bool {
67 self.game_id().is_some()
68 }
69
70 // /// setters
71 // fn set_player_id(&mut self, player_id: i64);
72 // fn set_username(&mut self, username: &str);
73 // fn set_avatar_url(&mut self, avatar_url: Option<String>);
74 // fn set_is_admin(&mut self, is_admin: bool);
75 // fn set_lobby_id(&mut self, lobby_id: &str);
76 // fn set_game_id(&mut self, game_id: &str);
77 // fn set_is_spectating(&mut self, is_spectating: bool);
78 // fn set_is_connected(&mut self, is_connected: bool);
79}
80
81#[cfg(test)]
82mod tests {
83 use super::PlayerIdentity;
84
85 struct Bare;
86 impl PlayerIdentity for Bare {
87 fn player_id(&self) -> i64 {
88 1
89 }
90 fn username(&self) -> &str {
91 "u"
92 }
93 fn avatar_url(&self) -> &str {
94 ""
95 }
96 }
97
98 #[test]
99 fn an_identity_that_resolves_no_name_reports_none() {
100 assert!(Bare.name().is_none());
101 }
102}