From 39189fec70489c487e6224fc554745f9d8ee9a7c Mon Sep 17 00:00:00 2001 From: MA Beaudet Date: Wed, 10 Nov 2021 14:24:16 +0100 Subject: [PATCH] feat: add better error handling for cards (following C-GOOD-ERR) --- src/card.rs | 116 +++++++++++++++++++++++--------------------------- src/errors.rs | 37 ++++++++++++++++ src/lib.rs | 5 ++- 3 files changed, 94 insertions(+), 64 deletions(-) create mode 100644 src/errors.rs diff --git a/src/card.rs b/src/card.rs index 47a8a2c..5595bf4 100644 --- a/src/card.rs +++ b/src/card.rs @@ -1,12 +1,12 @@ use std::str::FromStr; -use crate::{constants::PRIMES, MyError, Rules}; +use crate::{constants::PRIMES, errors::ParseCardError, Rules}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, Default, Clone, Hash)] +#[derive(Debug, Default, Clone, Hash, PartialEq)] pub struct Cards(Vec); impl Cards { @@ -49,14 +49,14 @@ impl Extend for Cards { } impl FromStr for Cards { - type Err = MyError; + type Err = ParseCardError; fn from_str(s: &str) -> Result { - Ok(Self( - s.trim() - .split_whitespace() - .map(|c| Card::from_str(c).expect("Could not parse card from string")) - .collect::>(), - )) + let mut cards = Cards::new(); + for c in s.trim().split_whitespace() { + let c = Card::from_str(c)?; + cards.0.push(c); + } + Ok(cards) } } @@ -93,7 +93,7 @@ impl Deck { &self.0 } - pub fn deal_with_rules(&mut self, rules: &Rules) -> Result { + pub fn deal_with_rules(&mut self, rules: &Rules) -> Result { let v = match rules { Rules::Classic => self.deal(5)?, @@ -106,16 +106,16 @@ impl Deck { &mut self, rules: &Rules, player_nb: usize, - ) -> Result, MyError> { + ) -> Result, ParseCardError> { (0..player_nb) .into_iter() .map(|_| Ok(self.deal_with_rules(rules)?)) .collect() } - pub fn deal(&mut self, n: usize) -> Result { + pub fn deal(&mut self, n: usize) -> Result { if self.0 .0.len() < n { - Err(MyError::IndexError) + Err(ParseCardError::Index) } else { Ok(Cards(self.0 .0.split_off(self.0 .0.len() - n))) } @@ -147,14 +147,6 @@ impl Default for Deck { } } -/// ``` -/// use poker_eval::{Card}; -/// use std::str::FromStr; -/// -/// assert_eq!( -/// Card::from_str("Kd").unwrap().get(), -/// 0b00001000_00000000_01001011_00100101_u32 -/// ); #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, Copy, Hash)] #[repr(transparent)] @@ -175,13 +167,6 @@ impl Card { } } - /// ``` - /// use poker_eval::{Card}; - /// use std::str::FromStr; - /// - /// let card = Card::from_str("Kd").unwrap(); - /// assert_eq!(card.rank(), 'K'); - /// ``` pub fn rank(&self) -> char { match (self.0 >> 8) & 0xF { 0 => '2', @@ -209,32 +194,22 @@ impl AsRef for Card { } impl TryFrom for Card { - type Error = MyError; + type Error = ParseCardError; fn try_from(value: u32) -> Result { let _ = match (value >> 8) & 0xF { 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 => Ok(()), - _ => Err(MyError::InvalidRank), + _ => Err(ParseCardError::U32), }; let _ = match (value >> 12) & 0xF { 0b1000 | 0b0100 | 0b0010 | 0b0001 => Ok(()), - _ => Err(MyError::InvalidSuit), + _ => Err(ParseCardError::U32), }; Ok(Self(value)) } } -/// ``` -/// use poker_eval::{Card}; -/// use std::str::FromStr; -/// -/// let card = Card::from_str("Kd").unwrap(); -/// let other = Card::from_str("Qd").unwrap(); -/// let other2 = Card::from_str("Ks").unwrap(); -/// assert!(card > other); -/// assert!(card <= other2); -/// ``` impl PartialOrd for Card { fn partial_cmp(&self, other: &Self) -> Option { ((self.0 >> 8) & 0xF).partial_cmp(&((other.0 >> 8) & 0xF)) @@ -247,14 +222,6 @@ impl Ord for Card { } } -/// ``` -/// use poker_eval::{Card}; -/// use std::str::FromStr; -/// -/// let card = Card::from_str("Kd").unwrap(); -/// let other = Card::from_str("Ks").unwrap(); -/// assert!(card == other); -/// ``` impl PartialEq for Card { fn eq(&self, other: &Self) -> bool { (self.0 >> 8) & 0xF == (other.0 >> 8) & 0xF @@ -264,15 +231,15 @@ impl PartialEq for Card { impl Eq for Card {} impl FromStr for Card { - type Err = MyError; + type Err = ParseCardError; fn from_str(s: &str) -> Result { let mut input = s.chars(); let (r, s) = (input.next(), input.next()); match (r, s) { - (None, None) => Err(MyError::InvalidLength), - (None, Some(_)) => Err(MyError::InvalidLength), - (Some(_), None) => Err(MyError::InvalidLength), + (None, None) => Err(ParseCardError::String), + (None, Some(_)) => Err(ParseCardError::String), + (Some(_), None) => Err(ParseCardError::String), (Some(r), Some(s)) => { let r = match r { '2' => Some(0), @@ -298,9 +265,9 @@ impl FromStr for Card { _ => None, }; match (r, s) { - (None, None) => Err(MyError::InvalidLength), - (None, Some(_)) => Err(MyError::InvalidRank), - (Some(_), None) => Err(MyError::InvalidSuit), + (None, None) => Err(ParseCardError::Chars), + (None, Some(_)) => Err(ParseCardError::Rank), + (Some(_), None) => Err(ParseCardError::Suit), (Some(r), Some(s)) => Ok(Self( PRIMES[r as usize] as u32 | (r << 8) | s | (1 << (16 + r)), )), @@ -310,13 +277,6 @@ impl FromStr for Card { } } -/// ``` -/// use poker_eval::{Card}; -/// use std::str::FromStr; -/// -/// let card = Card::from_str("Kd").unwrap(); -/// assert_eq!(card.to_string(), "Kd"); -/// ``` impl std::fmt::Display for Card { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}{}", self.rank(), self.suit()) @@ -338,3 +298,33 @@ fn test_sync() { assert_sync::(); assert_sync::(); } + +#[test] +fn test_fromstr() { + assert!(Cards::from_str("7s 3v").is_err()); + assert_eq!(Cards::from_str("7s 3v").unwrap_err(), ParseCardError::Suit); + assert_eq!(Cards::from_str("7s fv").unwrap_err(), ParseCardError::Chars); + assert_eq!(Cards::from_str("7s fd").unwrap_err(), ParseCardError::Rank); + assert!(Cards::from_str("7s 3d").is_ok()); + assert_eq!( + Cards::from_str("7s 3d").unwrap(), + Cards(vec![ + Card::from_str("7s").unwrap(), + Card::from_str("3d").unwrap() + ]) + ); + assert!(Card::from_str("3v").is_err()); + assert_eq!(Card::from_str("3v").unwrap_err(), ParseCardError::Suit); + assert_eq!(Card::from_str("fv").unwrap_err(), ParseCardError::Chars); + assert_eq!(Card::from_str("fd").unwrap_err(), ParseCardError::Rank); + assert_eq!( + *Card::from_str("Kd").unwrap().as_ref(), + 0b00001000_00000000_01001011_00100101_u32 + ); +} + +#[test] +fn test_tostr() { + let card = Card::from_str("Kd").unwrap(); + assert_eq!(card.to_string(), "Kd"); +} diff --git a/src/errors.rs b/src/errors.rs new file mode 100644 index 0000000..c4332dd --- /dev/null +++ b/src/errors.rs @@ -0,0 +1,37 @@ +#[derive(Debug, PartialEq, Eq)] +pub enum ParseCardError { + U32, + Rank, + Suit, + Chars, + String, + Index, +} + +impl std::error::Error for ParseCardError {} + +impl std::fmt::Display for ParseCardError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ParseCardError::U32 => write!(f, "Invalid u32 provided"), + ParseCardError::Rank => write!(f, "Invalid rank char provided"), + ParseCardError::Suit => write!(f, "Invalid suit char provided"), + ParseCardError::Chars => write!(f, "Invalid chars provided"), + ParseCardError::String => write!(f, "Unexpected string size"), + ParseCardError::Index => write!(f, "Index out of bounds"), + } + // write!(f, "Invalid number of cards") + } +} + +#[test] +fn test_send() { + fn assert_send() {} + assert_send::(); +} + +#[test] +fn test_sync() { + fn assert_sync() {} + assert_sync::(); +} diff --git a/src/lib.rs b/src/lib.rs index 34e6895..ed13f07 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,7 @@ //! pub mod card; pub mod constants; +pub mod errors; pub mod evaluator; use std::{ @@ -460,7 +461,9 @@ mod tests { #[test] fn evaluate_without_cards() { let mut deck = Deck::default(); - let player_hands = deck.deal_with_rules_and_player_nb(&Rules::Holdem, 4).unwrap(); + let player_hands = deck + .deal_with_rules_and_player_nb(&Rules::Holdem, 4) + .unwrap(); let mut evaluator = Evaluator::new(); assert!(evaluator.eval().is_err()); assert!(evaluator.hands(&player_hands).eval().is_err()); -- 2.20.1