open_pql/base/
suit.rs

1use super::*;
2
3/// Enum for Suits
4#[derive(
5    Copy, Clone, PartialEq, Eq, Debug, Ord, PartialOrd, Hash, Display, Default,
6)]
7pub enum Suit {
8    /// <span class="stab emoji">♠️</span> Spade
9    #[display("s")]
10    #[default]
11    S = 0,
12    /// <span class="stab emoji">♥️</span> Heart
13    #[display("h")]
14    H,
15    /// <span class="stab emoji">♦️</span> Diamond
16    #[display("d")]
17    D,
18    /// <span class="stab emoji">♣️</span> Club
19    #[display("c")]
20    C,
21}
22
23impl Suit {
24    /// All possible suits
25    pub const ARR_ALL: [Self; N_SUITS as usize] =
26        [Self::S, Self::H, Self::D, Self::C];
27
28    /// Creates a suit from a u8 value (0-3)
29    pub fn from_u8(v: u8) -> Self {
30        debug_assert!(v < N_SUITS, "invalid suit: {v}");
31        unsafe { mem::transmute(v) }
32    }
33
34    /// Creates a suit from a character
35    pub const fn from_char(c: char) -> Option<Self> {
36        match c {
37            'S' | 's' => Some(Self::S),
38            'H' | 'h' => Some(Self::H),
39            'D' | 'd' => Some(Self::D),
40            'C' | 'c' => Some(Self::C),
41            _ => None,
42        }
43    }
44}
45
46impl TryFrom<char> for Suit {
47    type Error = ParseError;
48
49    fn try_from(c: char) -> Result<Self, Self::Error> {
50        match c {
51            'S' | 's' => Ok(Self::S),
52            'H' | 'h' => Ok(Self::H),
53            'D' | 'd' => Ok(Self::D),
54            'C' | 'c' => Ok(Self::C),
55            _ => Err(ParseError::InvalidSuit(c.into())),
56        }
57    }
58}
59
60impl FromStr for Suit {
61    type Err = ParseError;
62
63    fn from_str(s: &str) -> Result<Self, Self::Err> {
64        let mut cs = s.chars().filter(|c| !c.is_whitespace());
65        if let Some(c) = cs.next()
66            && let Ok(s) = Self::try_from(c)
67            && cs.next().is_none()
68        {
69            return Ok(s);
70        }
71        Err(ParseError::InvalidSuit(s.into()))
72    }
73}
74
75impl From<Suit> for char {
76    fn from(value: Suit) -> Self {
77        match value {
78            Suit::S => 's',
79            Suit::H => 'h',
80            Suit::D => 'd',
81            Suit::C => 'c',
82        }
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89
90    impl Arbitrary for Suit {
91        fn arbitrary(g: &mut quickcheck::Gen) -> Self {
92            *g.choose(&Self::ARR_ALL).unwrap()
93        }
94    }
95
96    #[test]
97    fn test_consts() {
98        assert_eq!(Suit::ARR_ALL, [Suit::S, Suit::H, Suit::D, Suit::C]);
99    }
100
101    #[test]
102    fn test_as_int() {
103        assert_eq!(Suit::S as i8, 0);
104        assert_eq!(Suit::H as i8, 1);
105        assert_eq!(Suit::D as i8, 2);
106        assert_eq!(Suit::C as i8, 3);
107    }
108
109    #[test]
110    fn test_from_char() {
111        assert_eq!(Ok(Suit::S), 's'.try_into());
112        assert_eq!(Ok(Suit::H), 'h'.try_into());
113        assert_eq!(Ok(Suit::D), 'd'.try_into());
114        assert_eq!(Ok(Suit::C), 'c'.try_into());
115
116        assert_eq!(Ok(Suit::S), 'S'.try_into());
117        assert_eq!(Ok(Suit::H), 'H'.try_into());
118        assert_eq!(Ok(Suit::D), 'D'.try_into());
119        assert_eq!(Ok(Suit::C), 'C'.try_into());
120
121        assert_eq!(
122            Err(ParseError::InvalidSuit("?".into())),
123            Suit::try_from('?')
124        );
125    }
126
127    #[test]
128    fn test_from_char_option() {
129        assert_eq!(Some(Suit::S), Suit::from_char('s'));
130        assert_eq!(Some(Suit::H), Suit::from_char('h'));
131        assert_eq!(Some(Suit::D), Suit::from_char('d'));
132        assert_eq!(Some(Suit::C), Suit::from_char('c'));
133
134        assert_eq!(Some(Suit::S), Suit::from_char('S'));
135        assert_eq!(Some(Suit::H), Suit::from_char('H'));
136        assert_eq!(Some(Suit::D), Suit::from_char('D'));
137        assert_eq!(Some(Suit::C), Suit::from_char('C'));
138
139        assert_eq!(None, Suit::from_char('?'));
140        assert_eq!(None, Suit::from_char('1'));
141        assert_eq!(None, Suit::from_char('X'));
142    }
143
144    #[test]
145    fn test_from_str() {
146        assert_eq!(Ok(Suit::S), " s ".parse());
147        assert_eq!(
148            Err(ParseError::InvalidSuit("sS".into())),
149            "sS".parse::<Suit>()
150        );
151        assert!("".parse::<Suit>().is_err());
152        assert!("?".parse::<Suit>().is_err());
153    }
154
155    #[test]
156    fn test_to_string() {
157        assert_eq!("s", &Suit::S.to_string());
158        assert_eq!("h", &Suit::H.to_string());
159        assert_eq!("d", &Suit::D.to_string());
160        assert_eq!("c", &Suit::C.to_string());
161    }
162}