1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
//! Two different error types that may be encountered when trying to parse //! [`Card`] types from strings, or when trying to evaluate hands. //! //! The [`Debug`] representations aren't *particularly* helpful, so try to //! display errors as [`Display`](std::fmt::Display) when possible. use std::{error::Error, fmt}; use itertools::Itertools; use crate::card::Card; /// An error than can be thrown when parsing [`Card`] types from strings. /// /// # Examples /// /// An input with invalid length yields [`ParseCardError::InvalidLength`]. /// /// ``` /// use poker::{Card, ParseCardError}; /// let ten_of_clubs = "10c"; // not two characters; use 'T' instead of '10'! /// let result = ten_of_clubs.parse::<Card>(); /// assert_eq!( /// result, /// Err(ParseCardError::InvalidLength { /// original_input: "10c".into() /// }) /// ); /// ``` /// /// If an input *is* two characters, [`ParseCardError::InvalidRank`] will be /// thrown if the first character, representing the card's rank, is not one of /// '23456789TJQKA'. /// /// ``` /// use poker::{Card, ParseCardError}; /// let jack_of_clubs = "jc"; /// let result = jack_of_clubs.parse::<Card>(); /// assert_eq!( /// result, /// Err(ParseCardError::InvalidRank { /// original_input: "jc".into(), /// incorrect_char: 'j' /// }) /// ); /// ``` /// /// Similarly, a two-character input, with a second character representing the /// suit, does not contain one of "chsd", [`ParseCardError::InvalidSuit`] will /// be thrown. /// /// ``` /// use poker::{Card, ParseCardError}; /// let two_of_diamonds = "2D"; /// let result = two_of_diamonds.parse::<Card>(); /// assert_eq!( /// result, /// Err(ParseCardError::InvalidSuit { /// original_input: "2D".into(), /// incorrect_char: 'D' /// }) /// ); /// ``` #[derive(Debug, Clone, PartialEq, Eq)] pub enum ParseCardError { /// A string to be interpreted as a [`Card`] must be exactly two characters /// long. This variant is used if the input does not have meet this /// criterion. InvalidLength { /// The input that incited this error, converted to a [`String`] if /// needed. The original input is also used for calculating and /// reporting the incorrect number of characters received. original_input: String, }, /// A string to be interpreted as a [`Card`] must have its first character /// be one of "23456789TJQKA", otherwise, this error will be thrown and /// indicate the incorrect character. InvalidRank { /// The input that incited this error, converted to a [`String`] if /// needed. original_input: String, /// The actual character within the input that was unexpected and could /// not be interpreted as a card rank. incorrect_char: char, }, /// A string to be interpreted as a [`Card`] must have its first character /// be one of "23456789TJQKA", otherwise, this error will be thrown and /// indicate the incorrect character. InvalidSuit { /// The input that incited this error, converted to a [`String`] if /// needed. original_input: String, /// The actual character within the input that was unexpected and could /// not be interpreted as a card suit. incorrect_char: char, }, } impl fmt::Display for ParseCardError { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::InvalidLength { original_input } => write!( f, "Error parsing input '{}' as a Card: Found input of length {}, expected 2", original_input, original_input.len() ), Self::InvalidRank { original_input, incorrect_char, } => write!( f, "Error parsing input '{}': Invalid rank character '{}', expected one of \ [23456789TJQKA]", original_input, incorrect_char ), Self::InvalidSuit { original_input, incorrect_char, } => write!( f, "Error parsing input '{}': Invalid suit character '{}', expected one of [chsd]", original_input, incorrect_char ), } } } impl Error for ParseCardError {} /// An error that can be thrown when evaluating poker hands. /// /// # Examples /// /// If a group of [`Card`]s to be evaluated does not contain a set of unique /// cards, [`EvalError::CardsNotUnique`] will be thrown, as shown below: /// /// ``` /// use poker::{cards, Evaluator, EvalError}; /// let eval = Evaluator::new(); /// // Two king of clubs instead of a royal flush! /// let hand = cards!( /// Ten, Clubs; /// Jack, Clubs; /// Queen, Clubs; /// King, Clubs; /// King, Clubs /// ).to_vec(); /// let result = eval.evaluate(&hand); /// assert_eq!( /// result, /// Err(EvalError::CardsNotUnique(hand.clone())) /// ); /// ``` /// /// A group of 5 or more cards can be evaulated for the combination that yields /// the best possible poker hand, but four of less cards cannot be evaluated. /// This will throw [`EvalError::InvalidHandSize`]: /// /// ``` /// use poker::{cards, Evaluator, EvalError}; /// let eval = Evaluator::new(); /// let four_cards = cards!( /// Two, Clubs; /// Ten, Hearts; /// Seven, Hearts; /// Eight, Spades; /// ).to_vec(); /// let result = eval.evaluate(&four_cards); /// assert_eq!( /// result, /// Err(EvalError::InvalidHandSize(4)), /// ); /// ``` #[derive(Debug, Clone, PartialEq, Eq)] pub enum EvalError { /// This variant is used when the cards to be evaluated are not all unique. /// This captures the entire original hand, and the duplicates are /// calculated when reporting in [`Display`](std::fmt::Display) format. CardsNotUnique(Vec<Card>), /// This variant is used when the cards to be evaluated total to 4 or less. InvalidHandSize(usize), } impl fmt::Display for EvalError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::CardsNotUnique(cards) => { let dups: String = cards .iter() .counts() .into_iter() .filter_map(|(card, count)| { if count > 1 { Some(card.rank_suit_string()) } else { None } }) .collect::<Vec<_>>() .join(" "); write!( f, "Cannot evaluate a poker hand with a set of cards that are not unique. Cards \ duplicated at least once: {}", dups ) } Self::InvalidHandSize(size) => write!( f, "Cannot evaluate a poker hand with a set of less than 5 cards. Number of cards \ received: {}", size ), } } } impl Error for EvalError {}