1use super::*;
2
3#[derive(
5 Copy, Clone, PartialEq, Eq, Debug, Ord, PartialOrd, Hash, Display, Default,
6)]
7pub enum Suit {
8 #[display("s")]
10 #[default]
11 S = 0,
12 #[display("h")]
14 H,
15 #[display("d")]
17 D,
18 #[display("c")]
20 C,
21}
22
23impl Suit {
24 pub const ARR_ALL: [Self; N_SUITS as usize] =
26 [Self::S, Self::H, Self::D, Self::C];
27
28 pub fn from_u8(v: u8) -> Self {
30 debug_assert!(v < N_SUITS, "invalid suit: {v}");
31 unsafe { mem::transmute(v) }
32 }
33
34 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}