1use super::{Display, FromStr, Hash, N_RANKS, ParseError, mem};
2
3#[derive(
22 Copy, Clone, PartialEq, Eq, Debug, Ord, PartialOrd, Hash, Display, Default,
23)]
24pub enum Rank {
25 #[default]
27 #[display("2")]
28 R2 = 0,
29 #[display("3")]
31 R3,
32 #[display("4")]
34 R4,
35 #[display("5")]
37 R5,
38 #[display("6")]
40 R6,
41 #[display("7")]
43 R7,
44 #[display("8")]
46 R8,
47 #[display("9")]
49 R9,
50 #[display("T")]
52 RT,
53 #[display("J")]
55 RJ,
56 #[display("Q")]
58 RQ,
59 #[display("K")]
61 RK,
62 #[display("A")]
64 RA,
65}
66
67impl Rank {
68 pub const ARR_ALL: [Self; N_RANKS as usize] = [
70 Self::R2,
71 Self::R3,
72 Self::R4,
73 Self::R5,
74 Self::R6,
75 Self::R7,
76 Self::R8,
77 Self::R9,
78 Self::RT,
79 Self::RJ,
80 Self::RQ,
81 Self::RK,
82 Self::RA,
83 ];
84
85 pub const ARR_ALL_SHORT: [Self; 9] = [
87 Self::R6,
88 Self::R7,
89 Self::R8,
90 Self::R9,
91 Self::RT,
92 Self::RJ,
93 Self::RQ,
94 Self::RK,
95 Self::RA,
96 ];
97
98 pub(crate) fn from_u8(v: u8) -> Self {
100 debug_assert!(v < N_RANKS, "invalid rank: {v}");
101 unsafe { mem::transmute(v) }
102 }
103
104 pub const fn from_char(c: char) -> Option<Self> {
105 match c {
106 '2' => Some(Self::R2),
107 '3' => Some(Self::R3),
108 '4' => Some(Self::R4),
109 '5' => Some(Self::R5),
110 '6' => Some(Self::R6),
111 '7' => Some(Self::R7),
112 '8' => Some(Self::R8),
113 '9' => Some(Self::R9),
114 't' | 'T' => Some(Self::RT),
115 'j' | 'J' => Some(Self::RJ),
116 'q' | 'Q' => Some(Self::RQ),
117 'k' | 'K' => Some(Self::RK),
118 'a' | 'A' => Some(Self::RA),
119 _ => None,
120 }
121 }
122}
123
124impl From<Rank> for char {
125 fn from(value: Rank) -> Self {
126 match value {
127 Rank::R2 => '2',
128 Rank::R3 => '3',
129 Rank::R4 => '4',
130 Rank::R5 => '5',
131 Rank::R6 => '6',
132 Rank::R7 => '7',
133 Rank::R8 => '8',
134 Rank::R9 => '9',
135 Rank::RT => 'T',
136 Rank::RJ => 'J',
137 Rank::RQ => 'Q',
138 Rank::RK => 'K',
139 Rank::RA => 'A',
140 }
141 }
142}
143
144impl TryFrom<char> for Rank {
145 type Error = ParseError;
146
147 fn try_from(c: char) -> Result<Self, Self::Error> {
148 match c {
149 '2' => Ok(Self::R2),
150 '3' => Ok(Self::R3),
151 '4' => Ok(Self::R4),
152 '5' => Ok(Self::R5),
153 '6' => Ok(Self::R6),
154 '7' => Ok(Self::R7),
155 '8' => Ok(Self::R8),
156 '9' => Ok(Self::R9),
157 'T' | 't' => Ok(Self::RT),
158 'J' | 'j' => Ok(Self::RJ),
159 'Q' | 'q' => Ok(Self::RQ),
160 'K' | 'k' => Ok(Self::RK),
161 'A' | 'a' => Ok(Self::RA),
162 _ => Err(ParseError::InvalidRank(c.into())),
163 }
164 }
165}
166
167impl FromStr for Rank {
168 type Err = ParseError;
169
170 fn from_str(s: &str) -> Result<Self, Self::Err> {
171 let mut cs = s.chars().filter(|c| !c.is_whitespace());
172 if let Some(c) = cs.next()
173 && let Ok(r) = Self::try_from(c)
174 && cs.next().is_none()
175 {
176 return Ok(r);
177 }
178 Err(ParseError::InvalidRank(s.into()))
179 }
180}
181
182#[cfg(test)]
183mod tests {
184 use super::*;
185 use crate::*;
186
187 impl Arbitrary for Rank {
188 fn arbitrary(g: &mut quickcheck::Gen) -> Self {
189 *g.choose(&Self::ARR_ALL).unwrap()
190 }
191 }
192
193 #[test]
194 fn test_consts() {
195 assert_eq!(
196 Rank::ARR_ALL,
197 [
198 Rank::R2,
199 Rank::R3,
200 Rank::R4,
201 Rank::R5,
202 Rank::R6,
203 Rank::R7,
204 Rank::R8,
205 Rank::R9,
206 Rank::RT,
207 Rank::RJ,
208 Rank::RQ,
209 Rank::RK,
210 Rank::RA,
211 ]
212 );
213 }
214
215 #[test]
216 fn test_as_int() {
217 assert_eq!(Rank::R2 as i8, 0);
218 assert_eq!(Rank::R3 as i8, 1);
219 assert_eq!(Rank::R4 as i8, 2);
220 assert_eq!(Rank::R5 as i8, 3);
221 assert_eq!(Rank::R6 as i8, 4);
222 assert_eq!(Rank::R7 as i8, 5);
223 assert_eq!(Rank::R8 as i8, 6);
224 assert_eq!(Rank::R9 as i8, 7);
225 assert_eq!(Rank::RT as i8, 8);
226 assert_eq!(Rank::RJ as i8, 9);
227 assert_eq!(Rank::RQ as i8, 10);
228 assert_eq!(Rank::RK as i8, 11);
229 assert_eq!(Rank::RA as i8, 12);
230 }
231
232 #[test]
233 fn test_from_char() {
234 assert_eq!(Ok(Rank::R2), '2'.try_into());
235 assert_eq!(Ok(Rank::R3), '3'.try_into());
236 assert_eq!(Ok(Rank::R4), '4'.try_into());
237 assert_eq!(Ok(Rank::R5), '5'.try_into());
238 assert_eq!(Ok(Rank::R6), '6'.try_into());
239 assert_eq!(Ok(Rank::R7), '7'.try_into());
240 assert_eq!(Ok(Rank::R8), '8'.try_into());
241 assert_eq!(Ok(Rank::R9), '9'.try_into());
242
243 assert_eq!(Ok(Rank::RT), 'T'.try_into());
244 assert_eq!(Ok(Rank::RJ), 'J'.try_into());
245 assert_eq!(Ok(Rank::RQ), 'Q'.try_into());
246 assert_eq!(Ok(Rank::RK), 'K'.try_into());
247 assert_eq!(Ok(Rank::RA), 'A'.try_into());
248
249 assert_eq!(Ok(Rank::RT), 't'.try_into());
250 assert_eq!(Ok(Rank::RJ), 'j'.try_into());
251 assert_eq!(Ok(Rank::RQ), 'q'.try_into());
252 assert_eq!(Ok(Rank::RK), 'k'.try_into());
253 assert_eq!(Ok(Rank::RA), 'a'.try_into());
254
255 assert_eq!(
256 Err(ParseError::InvalidRank("?".into())),
257 Rank::try_from('?')
258 );
259 }
260
261 #[test]
262 fn test_from_char_option() {
263 assert_eq!(Some(Rank::R2), Rank::from_char('2'));
264 assert_eq!(Some(Rank::R3), Rank::from_char('3'));
265 assert_eq!(Some(Rank::R4), Rank::from_char('4'));
266 assert_eq!(Some(Rank::R5), Rank::from_char('5'));
267 assert_eq!(Some(Rank::R6), Rank::from_char('6'));
268 assert_eq!(Some(Rank::R7), Rank::from_char('7'));
269 assert_eq!(Some(Rank::R8), Rank::from_char('8'));
270 assert_eq!(Some(Rank::R9), Rank::from_char('9'));
271
272 assert_eq!(Some(Rank::RT), Rank::from_char('T'));
273 assert_eq!(Some(Rank::RJ), Rank::from_char('J'));
274 assert_eq!(Some(Rank::RQ), Rank::from_char('Q'));
275 assert_eq!(Some(Rank::RK), Rank::from_char('K'));
276 assert_eq!(Some(Rank::RA), Rank::from_char('A'));
277
278 assert_eq!(Some(Rank::RT), Rank::from_char('t'));
279 assert_eq!(Some(Rank::RJ), Rank::from_char('j'));
280 assert_eq!(Some(Rank::RQ), Rank::from_char('q'));
281 assert_eq!(Some(Rank::RK), Rank::from_char('k'));
282 assert_eq!(Some(Rank::RA), Rank::from_char('a'));
283
284 assert_eq!(None, Rank::from_char('?'));
285 assert_eq!(None, Rank::from_char('1'));
286 assert_eq!(None, Rank::from_char('X'));
287 }
288
289 #[test]
290 fn test_from_str() {
291 assert_eq!(Ok(Rank::R2), " 2 ".parse());
292 assert_eq!(
293 Err(ParseError::InvalidRank("23".into())),
294 "23".parse::<Rank>()
295 );
296 assert!("".parse::<Rank>().is_err());
297 assert!("?".parse::<Rank>().is_err());
298 }
299
300 #[test]
301 fn test_to_string() {
302 assert_eq!("2", &Rank::R2.to_string());
303 assert_eq!("3", &Rank::R3.to_string());
304 assert_eq!("4", &Rank::R4.to_string());
305 assert_eq!("5", &Rank::R5.to_string());
306 assert_eq!("6", &Rank::R6.to_string());
307 assert_eq!("7", &Rank::R7.to_string());
308 assert_eq!("8", &Rank::R8.to_string());
309 assert_eq!("9", &Rank::R9.to_string());
310 assert_eq!("T", &Rank::RT.to_string());
311 assert_eq!("J", &Rank::RJ.to_string());
312 assert_eq!("Q", &Rank::RQ.to_string());
313 assert_eq!("K", &Rank::RK.to_string());
314 assert_eq!("A", &Rank::RA.to_string());
315 }
316
317 #[test]
318 fn test_to_char() {
319 let cs = "23456789TJQKA";
320 for (i, &r) in Rank::ARR_ALL.iter().enumerate() {
321 assert_eq!(cs.chars().nth(i).unwrap(), char::from(r));
322 }
323 }
324}