Skip to main content

server/domain/types/
pending.rs

1use std::collections::HashSet;
2
3use itertools::Itertools;
4use serde::{Deserialize, Serialize};
5
6use crate::domain::{PLAYER0, PLAYER1, Player};
7
8/// Represents players who are to acknowledge the current game phase before
9/// the game can proceed to the next phase.
10///
11/// `Pending` is used to implement synchronous turn progression:
12/// a phase (e.g. bidding, playing a card, revealing tricks) only advances
13/// when **all** expected players have called `acknowledge()`.
14///
15/// An empty set means the phase is complete and the game may proceed.
16///
17/// Note: there is now `new` as the `default` pending state will be pending
18/// for both players in the game.
19#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
20#[serde(transparent)]
21pub struct Pending(HashSet<Player>);
22
23/// Trait for game states that track player acknowledgments.
24pub trait HasPending {
25    /// Immutable access to the pending set.
26    fn pending(&self) -> &Pending;
27
28    /// Mutable access to the pending set.
29    fn pending_mut(&mut self) -> &mut Pending;
30}
31
32impl Pending {
33    /// Returns `true` if all players have acknowledged (i.e. phase is complete).
34    #[must_use]
35    #[inline]
36    pub fn finished(&self) -> bool {
37        self.0.is_empty()
38    }
39
40    /// Returns `true` if the given player still needs to acknowledge.
41    #[must_use]
42    #[inline]
43    pub fn waiting_on(&self, player: Player) -> bool {
44        self.0.contains(&player)
45    }
46
47    /// Registers acknowledgment from a player.
48    ///
49    /// Returns `true` if both players have acknowledged and play can continue.
50    #[must_use]
51    pub fn acknowledge(&mut self, player: Player) -> bool {
52        self.0.remove(&player);
53        self.0.is_empty()
54    }
55}
56
57impl Default for Pending {
58    fn default() -> Self {
59        Self(HashSet::from([PLAYER0, PLAYER1]))
60    }
61}
62
63#[cfg(test)]
64impl From<&[Player]> for Pending {
65    fn from(value: &[Player]) -> Self {
66        Self(HashSet::from_iter(value.to_owned()))
67    }
68}
69
70impl std::fmt::Display for Pending {
71    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72        let pending = self.0.iter().map(|p| p.to_string()).join(", ");
73        write!(f, "Pending({pending})")
74    }
75}