diff --git a/Cargo.toml b/Cargo.toml index bf0a018..34f9a64 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "cardpack" description = "Generic Deck of Cards" -version = "0.4.12" +version = "0.4.13" authors = ["folkengine "] repository = "https://github.com/ContractBridge/cardpack.rs.git" homepage = "https://github.com/ContractBridge/cardpack.rs" diff --git a/src/cards/decks/standard52.rs b/src/cards/decks/standard52.rs index 14a9e0c..c08921d 100644 --- a/src/cards/decks/standard52.rs +++ b/src/cards/decks/standard52.rs @@ -157,9 +157,32 @@ impl Standard52 { } #[must_use] - pub fn card_from_index(card_str: &'static str) -> Card { - let rank = Rank::from_french_deck_index(Standard52::rank_str_from_index(card_str)); - let suit = Suit::from_french_deck_index(Standard52::suit_char_from_index(card_str)); + pub fn card_from_index(index: &'static str) -> Card { + let rank = Rank::from_french_deck_index(Standard52::rank_str_from_index(index)); + let suit = Suit::from_french_deck_index(Standard52::suit_char_from_index(index)); + + if rank.is_blank() || suit.is_blank() { + Card::blank_card() + } else { + Card::new(rank, suit) + } + } + + #[must_use] + #[allow(clippy::needless_pass_by_value)] + pub fn card_from_string(index: String) -> Card { + let char_vec: Vec = index.chars().collect(); + + let mut rank = Rank::default(); + let mut suit = Suit::default(); + + if let Some(r) = char_vec.get(0) { + rank = Rank::from_french_deck_char(*r); + } + + if let Some(s) = char_vec.get(1) { + suit = Suit::from_french_deck_index(*s); + } if rank.is_blank() || suit.is_blank() { Card::blank_card() @@ -411,6 +434,31 @@ mod standard52_tests { assert_eq!(Card::blank_card(), Standard52::card_from_index(input)); } + #[rstest] + #[case(String::from("2S"), Card::from_index_strings(TWO, SPADES))] + #[case(String::from("2s"), Card::from_index_strings(TWO, SPADES))] + #[case(String::from("2♠"), Card::from_index_strings(TWO, SPADES))] + #[case(String::from("3S"), Card::from_index_strings(THREE, SPADES))] + #[case(String::from("3♠"), Card::from_index_strings(THREE, SPADES))] + #[case(String::from("4♠"), Card::from_index_strings(FOUR, SPADES))] + #[case(String::from("4S"), Card::from_index_strings(FOUR, SPADES))] + #[case(String::from("5♠"), Card::from_index_strings(FIVE, SPADES))] + #[case(String::from("5S"), Card::from_index_strings(FIVE, SPADES))] + fn card_from_string(#[case] input: String, #[case] expected: Card) { + assert_eq!(expected, Standard52::card_from_string(input)); + } + + #[rstest] + #[case(String::from("XX"))] + #[case(String::from("2X"))] + #[case(String::from("XS"))] + #[case(String::from(" "))] + #[case(String::from(" "))] + #[case(String::from(""))] + fn card_from_string__invalid_index(#[case] input: String) { + assert_eq!(Card::blank_card(), Standard52::card_from_string(input)); + } + #[test] fn sort_by_suit() { let pile = Standard52::pile_from_index("2S 3S 9S TS QS JH Ac").unwrap(); diff --git a/src/cards/rank.rs b/src/cards/rank.rs index 655216f..2f547fe 100644 --- a/src/cards/rank.rs +++ b/src/cards/rank.rs @@ -192,6 +192,27 @@ impl Rank { } } + /// Returns a Rank entity based on its index string. + #[must_use] + pub fn from_french_deck_char(index: char) -> Rank { + match index { + 'A' | 'a' => Rank::new(ACE), + 'K' | 'k' => Rank::new(KING), + 'Q' | 'q' => Rank::new(QUEEN), + 'J' | 'j' => Rank::new(JACK), + 'T' | 't' | '0' => Rank::new(TEN), + '9' => Rank::new(NINE), + '8' => Rank::new(EIGHT), + '7' => Rank::new(SEVEN), + '6' => Rank::new(SIX), + '5' => Rank::new(FIVE), + '4' => Rank::new(FOUR), + '3' => Rank::new(THREE), + '2' => Rank::new(TWO), + _ => Rank::new(BLANK_RANK), + } + } + #[must_use] pub fn generate_canasta_ranks() -> Vec { Rank::from_array(&[ @@ -353,6 +374,32 @@ mod rank_tests { assert_eq!(expected, Rank::from_array(&[KING, QUEEN])); } + #[rstest] + #[case('A', Rank::new(ACE))] + #[case('a', Rank::new(ACE))] + #[case('K', Rank::new(KING))] + #[case('k', Rank::new(KING))] + #[case('Q', Rank::new(QUEEN))] + #[case('q', Rank::new(QUEEN))] + #[case('J', Rank::new(JACK))] + #[case('j', Rank::new(JACK))] + #[case('T', Rank::new(TEN))] + #[case('t', Rank::new(TEN))] + #[case('0', Rank::new(TEN))] + #[case('9', Rank::new(NINE))] + #[case('8', Rank::new(EIGHT))] + #[case('7', Rank::new(SEVEN))] + #[case('6', Rank::new(SIX))] + #[case('5', Rank::new(FIVE))] + #[case('4', Rank::new(FOUR))] + #[case('3', Rank::new(THREE))] + #[case('2', Rank::new(TWO))] + #[case('_', Rank::new(BLANK_RANK))] + #[case(' ', Rank::new(BLANK_RANK))] + fn from_french_deck_char(#[case] input: char, #[case] expected: Rank) { + assert_eq!(expected, Rank::from_french_deck_char(input)); + } + #[rstest] #[case("JB", Rank::new(BIG_JOKER))] #[case("JL", Rank::new(LITTLE_JOKER))]