Skip to main content

server/domain/types/card/
face.rs

1use serde::{Deserialize, Serialize};
2use strum::EnumIter;
3
4use super::{rank::Rank, value::Value};
5use crate::domain::CardsError;
6
7/// The face of a playing card (Ace through King).
8#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, EnumIter)]
9#[rustfmt::skip]
10pub enum Face {
11    #[doc(hidden)] Ace,
12    #[doc(hidden)] Two,
13    #[doc(hidden)] Three,
14    #[doc(hidden)] Four,
15    #[doc(hidden)] Five,
16    #[doc(hidden)] Six,
17    #[doc(hidden)] Seven,
18    #[doc(hidden)] Eight,
19    #[doc(hidden)] Nine,
20    #[doc(hidden)] Ten,
21    #[doc(hidden)] Jack,
22    #[doc(hidden)] Queen,
23    #[doc(hidden)] King,
24}
25
26impl Face {
27    /// Returns `true` if this face is a Jack.
28    #[must_use]
29    #[inline]
30    pub const fn is_jack(&self) -> bool {
31        matches!(self, Self::Jack)
32    }
33
34    /// Returns the rank used for ordering (Ace low, King high).
35    #[must_use]
36    pub fn rank(&self) -> Rank {
37        let rank = match self {
38            Self::Ace => 1,
39            Self::Two => 2,
40            Self::Three => 3,
41            Self::Four => 4,
42            Self::Five => 5,
43            Self::Six => 6,
44            Self::Seven => 7,
45            Self::Eight => 8,
46            Self::Nine => 9,
47            Self::Ten => 10,
48            Self::Jack => 11,
49            Self::Queen => 12,
50            Self::King => 13,
51        };
52
53        Rank::from(rank)
54    }
55
56    /// Returns the point or face value.
57    ///
58    /// - Ace = 1 (low)
59    /// - 2–10 = face value
60    /// - Jack/Queen/King = 10
61    #[must_use]
62    pub fn value(&self) -> Value {
63        let value = match self {
64            Self::Ace => 1,
65            Self::Two => 2,
66            Self::Three => 3,
67            Self::Four => 4,
68            Self::Five => 5,
69            Self::Six => 6,
70            Self::Seven => 7,
71            Self::Eight => 8,
72            Self::Nine => 9,
73            Self::Ten | Self::Jack | Self::Queen | Self::King => 10,
74        };
75
76        Value::from(value)
77    }
78}
79
80impl TryFrom<char> for Face {
81    type Error = CardsError;
82
83    fn try_from(value: char) -> Result<Self, Self::Error> {
84        match value {
85            'A' => Ok(Self::Ace),
86            '2' => Ok(Self::Two),
87            '3' => Ok(Self::Three),
88            '4' => Ok(Self::Four),
89            '5' => Ok(Self::Five),
90            '6' => Ok(Self::Six),
91            '7' => Ok(Self::Seven),
92            '8' => Ok(Self::Eight),
93            '9' => Ok(Self::Nine),
94            'T' => Ok(Self::Ten),
95            'J' => Ok(Self::Jack),
96            'Q' => Ok(Self::Queen),
97            'K' => Ok(Self::King),
98            other => Err(CardsError::InvalidCard(format!("Invalid face: {other}"))),
99        }
100    }
101}
102
103impl std::fmt::Display for Face {
104    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105        let s = match self {
106            Self::Ace => "A",
107            Self::Two => "2",
108            Self::Three => "3",
109            Self::Four => "4",
110            Self::Five => "5",
111            Self::Six => "6",
112            Self::Seven => "7",
113            Self::Eight => "8",
114            Self::Nine => "9",
115            Self::Ten => "T",
116            Self::Jack => "J",
117            Self::Queen => "Q",
118            Self::King => "K",
119        };
120        s.fmt(f)
121    }
122}
123
124#[cfg(test)]
125#[coverage(off)]
126pub mod test {
127    use super::*;
128
129    #[test]
130    fn face_has_rank_value_display_string_and_name() {
131        let faces = [
132            (Face::Ace, 1, 1, "A"),
133            (Face::Two, 2, 2, "2"),
134            (Face::Three, 3, 3, "3"),
135            (Face::Four, 4, 4, "4"),
136            (Face::Five, 5, 5, "5"),
137            (Face::Six, 6, 6, "6"),
138            (Face::Seven, 7, 7, "7"),
139            (Face::Eight, 8, 8, "8"),
140            (Face::Nine, 9, 9, "9"),
141            (Face::Ten, 10, 10, "T"),
142            (Face::Jack, 11, 10, "J"),
143            (Face::Queen, 12, 10, "Q"),
144            (Face::King, 13, 10, "K"),
145        ];
146
147        for (face, rank, value, display_string) in faces {
148            assert_eq!(face.rank(), Rank::from(rank));
149            assert_eq!(face.value(), Value::from(value));
150            assert_eq!(face.to_string(), display_string);
151        }
152    }
153}