Skip to main content

playin_cards/
format.rs

1use std::fmt;
2use std::fmt::Display;
3
4use crate::{Card, Color, Rank, Suit};
5
6impl From<Rank> for char {
7    /// Convert Rank to `char` output values are in `A23456789TJQK`
8    fn from(rank: Rank) -> Self {
9        match rank {
10            Rank::Ace => 'A',
11            Rank::Two => '2',
12            Rank::Three => '3',
13            Rank::Four => '4',
14            Rank::Five => '5',
15            Rank::Six => '6',
16            Rank::Seven => '7',
17            Rank::Eight => '8',
18            Rank::Nine => '9',
19            Rank::Ten => 'T',
20            Rank::Jack => 'J',
21            Rank::Queen => 'Q',
22            Rank::King => 'K',
23        }
24    }
25}
26
27impl From<Suit> for char {
28    /// Convert `Suit` to `char`. Output values are one of `♥♦♠♣`
29    fn from(suit: Suit) -> Self {
30        match suit {
31            Suit::Hearts => '♥',
32            Suit::Diamonds => '♦',
33            Suit::Spades => '♠',
34            Suit::Clubs => '♣',
35        }
36    }
37}
38
39impl From<Color> for char {
40    fn from(c: Color) -> char {
41        match c {
42            Color::Red => 'R',
43            Color::Black => 'B',
44        }
45    }
46}
47
48impl fmt::Display for Rank {
49    /// Format a Rank as a single ASCII character in `A23456789TJQK`
50    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
51        char::from(*self).fmt(f)
52    }
53}
54
55impl fmt::Display for Suit {
56    /// Format a Suit as a single Unicode suit char: e.g `♠`, `♥`, `♦`, or `♣`
57    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
58        char::from(*self).fmt(f)
59    }
60}
61
62impl fmt::LowerHex for Suit {
63    /// Format a Suit as a single ASCII char: `H`, `D`, `S`, or `C`
64    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
65        match self {
66            Self::Hearts => 'H',
67            Self::Diamonds => 'D',
68            Self::Spades => 'S',
69            Self::Clubs => 'C',
70        }
71        .fmt(f)
72    }
73}
74
75impl fmt::Display for Color {
76    /// Format a `Color` as a single ASCII char: `R` for `Red`, `B` for `Black`
77    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
78        char::from(*self).fmt(f)
79    }
80}
81
82impl fmt::Display for Card {
83    /// Format a Card as a Rank, represented by a single char in `A2345678TJQK`,
84    /// and a suit, represented as a single Unicode char in `♠♥♦♣`. e.g. `A♣`
85    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
86        match self {
87            Self::Regular { rank, suit } => write!(f, "{rank}{suit}"),
88            Self::Joker(color) => write!(f, "{}J", color),
89        }
90    }
91}
92
93impl fmt::UpperHex for Card {
94    /// Format a `Card` as a single Unicode card character, e.g. 🃞
95    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
96        match self {
97            Self::Joker(color) => match color {
98                Color::Red => write!(f, "🂿"),
99                Color::Black => write!(f, "🃏"),
100            },
101            Self::Regular { rank, suit } => {
102                let suit: u32 = match suit {
103                    Suit::Spades => 0x1F0A,
104                    Suit::Hearts => 0x1F0B,
105                    Suit::Diamonds => 0x1F0C,
106                    Suit::Clubs => 0x1F0D,
107                };
108                let rank: u32 = match rank {
109                    // Have to skip the Knight rank from Unicode
110                    Rank::Queen => 0x0D,
111                    Rank::King => 0x0E,
112                    Rank::Ace => 0x01,
113                    _ => *rank as u32,
114                };
115                let codepoint: u32 = (suit << 4) + rank;
116                write!(f, "{}", char::from_u32(codepoint).unwrap())
117            }
118        }
119    }
120}
121
122/// Format a Card using a ASCII suit char: e.g `H`, `D`, `S`, or `C`
123impl fmt::LowerHex for Card {
124    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125        match self {
126            Self::Regular { rank, suit } => write!(f, "{rank}{suit:x}"),
127            Self::Joker(color) => write!(f, "{}J", color),
128        }
129    }
130}
131
132#[cfg(test)]
133pub mod tests {
134    use super::*;
135    use crate::test::FULL_DECK_CARDS;
136
137    // A full Deck of cards in suit-rank order
138    #[rustfmt::skip]
139    pub const ASCII_REPRS: [&str; 52] = [
140        "AH", "2H", "3H", "4H", "5H", "6H", "7H", "8H", "9H", "TH", "JH", "QH", "KH",
141        "AD", "2D", "3D", "4D", "5D", "6D", "7D", "8D", "9D", "TD", "JD", "QD", "KD",
142        "AS", "2S", "3S", "4S", "5S", "6S", "7S", "8S", "9S", "TS", "JS", "QS", "KS",
143        "AC", "2C", "3C", "4C", "5C", "6C", "7C", "8C", "9C", "TC", "JC", "QC", "KC",
144    ];
145    // Represents an ordered deck as 2-char mixed
146    #[rustfmt::skip]
147    pub const MIXED_REPRS: [&str; 52] = [
148        "A♥", "2♥", "3♥", "4♥", "5♥", "6♥", "7♥", "8♥", "9♥", "T♥", "J♥", "Q♥", "K♥",
149        "A♦", "2♦", "3♦", "4♦", "5♦", "6♦", "7♦", "8♦", "9♦", "T♦", "J♦", "Q♦", "K♦",
150        "A♠", "2♠", "3♠", "4♠", "5♠", "6♠", "7♠", "8♠", "9♠", "T♠", "J♠", "Q♠", "K♠",
151        "A♣", "2♣", "3♣", "4♣", "5♣", "6♣", "7♣", "8♣", "9♣", "T♣", "J♣", "Q♣", "K♣",
152    ];
153    // Represents an ordered deck as single-char Unicode
154    #[rustfmt::skip]
155    pub const UNICODE_REPRS: &str = "\
156        \u{1F0B1}\u{1F0B2}\u{1F0B3}\u{1F0B4}\u{1F0B5}\u{1F0B6}\u{1F0B7}\u{1F0B8}\u{1F0B9}\
157        \u{1F0BA}\u{1F0BB}\u{1F0BD}\u{1F0BE}\
158        \u{1F0C1}\u{1F0C2}\u{1F0C3}\u{1F0C4}\u{1F0C5}\u{1F0C6}\u{1F0C7}\u{1F0C8}\u{1F0C9}\
159        \u{1F0CA}\u{1F0CB}\u{1F0CD}\u{1F0CE}\
160        \u{1F0A1}\u{1F0A2}\u{1F0A3}\u{1F0A4}\u{1F0A5}\u{1F0A6}\u{1F0A7}\u{1F0A8}\u{1F0A9}\
161        \u{1F0AA}\u{1F0AB}\u{1F0AD}\u{1F0AE}\
162        \u{1F0D1}\u{1F0D2}\u{1F0D3}\u{1F0D4}\u{1F0D5}\u{1F0D6}\u{1F0D7}\u{1F0D8}\u{1F0D9}\
163        \u{1F0DA}\u{1F0DB}\u{1F0DD}\u{1F0DE}";
164
165    #[test]
166    fn card_mixed() {
167        for (card, exp_repr) in FULL_DECK_CARDS.iter().zip(MIXED_REPRS.iter()) {
168            assert_eq!(
169                &format!("{}", card),
170                exp_repr,
171                "Unexpected format for card {}. expected {}",
172                card,
173                exp_repr
174            );
175        }
176        assert_eq!(format!("{}", Card::Joker(Color::Black)), "BJ");
177        assert_eq!(format!("{}", Card::Joker(Color::Red)), "RJ");
178    }
179    #[test]
180    fn card_unicode() {
181        for (card, exp_repr) in FULL_DECK_CARDS.iter().zip(UNICODE_REPRS.chars()) {
182            assert_eq!(
183                format!("{card:X}"),
184                exp_repr.to_string(),
185                "Unexpected format for card {}. Expected {}",
186                card,
187                exp_repr
188            );
189        }
190    }
191    #[test]
192    fn card_ascii() {
193        for (card, exp_repr) in FULL_DECK_CARDS.iter().zip(ASCII_REPRS) {
194            assert_eq!(
195                format!("{card:x}"),
196                exp_repr.to_string(),
197                "Unexpected format for card {}. Expected {}",
198                card,
199                exp_repr
200            );
201        }
202    }
203
204    #[test]
205    fn rank() {
206        assert_eq!("A", format!("{}", Rank::Ace));
207        assert_eq!("2", format!("{}", Rank::Two));
208        assert_eq!("3", format!("{}", Rank::Three));
209        assert_eq!("6", format!("{}", Rank::Six));
210        assert_eq!("9", format!("{}", Rank::Nine));
211        assert_eq!("T", format!("{}", Rank::Ten));
212        assert_eq!("J", format!("{}", Rank::Jack));
213        assert_eq!("Q", format!("{}", Rank::Queen));
214        assert_eq!("K", format!("{}", Rank::King));
215    }
216    #[test]
217    fn suit() {
218        assert_eq!("♠", format!("{}", Suit::Spades));
219        assert_eq!("♥", format!("{}", Suit::Hearts));
220        assert_eq!("♦", format!("{}", Suit::Diamonds));
221        assert_eq!("♣", format!("{}", Suit::Clubs));
222    }
223    #[test]
224    fn suit_ascii() {
225        assert_eq!("S", format!("{:x}", Suit::Spades));
226        assert_eq!("H", format!("{:x}", Suit::Hearts));
227        assert_eq!("D", format!("{:x}", Suit::Diamonds));
228        assert_eq!("C", format!("{:x}", Suit::Clubs));
229    }
230}