diff --git a/Cargo.toml b/Cargo.toml index 804ae2b..92756a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "cardpack" description = "Generic Deck of Cards" -version = "0.4.4" +version = "0.4.5" authors = ["folkengine "] repository = "https://github.com/ContractBridge/cardpack.rs.git" homepage = "https://github.com/ContractBridge/cardpack.rs" diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 0000000..0c30ee2 --- /dev/null +++ b/clippy.toml @@ -0,0 +1,3 @@ +msrv = "1.56.0" + +cognitive-complexity-threshold = 30 diff --git a/src/cards/card.rs b/src/cards/card.rs index fda1698..2313b6e 100644 --- a/src/cards/card.rs +++ b/src/cards/card.rs @@ -1,9 +1,9 @@ -use colored::*; +use colored::Colorize; use std::fmt; use unic_langid::LanguageIdentifier; -use crate::cards::rank::*; -use crate::cards::suit::*; +use crate::cards::rank::Rank; +use crate::cards::suit::Suit; use crate::Named; pub const BLANK: &str = "blank"; @@ -25,6 +25,7 @@ pub struct Card { impl Card { /// Instantiates a new Card with the default weight as defined in the fluent /// templates. + #[must_use] pub fn new(rank: &'static str, suit: &'static str) -> Card { let suit = Suit::new(suit); let rank = Rank::new(rank); @@ -40,18 +41,20 @@ impl Card { /// Instantiates a Card with the weight determined by the passed in Rank and /// Suit. + #[must_use] pub fn new_from_structs(rank: Rank, suit: Suit) -> Card { let weight = Card::determine_weight(&suit, &rank); let index = Card::determine_index(&suit, &rank); Card { weight, index, - rank, suit, + rank, } } /// Returns a Symbol String for the Card. + #[must_use] pub fn symbol(&self, lid: &LanguageIdentifier) -> String { let rank = self.rank.index(lid); let suit = self.suit.symbol(); @@ -59,25 +62,26 @@ impl Card { } /// Returns a Symbol String for the Card in the traditional colors for the Suits. + #[must_use] pub fn symbol_colorized(&self, lid: &LanguageIdentifier) -> String { let rank = self.rank.index(lid); let suit = self.suit.symbol(); match self.suit.name() { - "hearts" => format!("{}{}", rank, suit).red().to_string(), - "diamonds" => format!("{}{}", rank, suit).red().to_string(), + "hearts" | "herz" | "diamonds" => format!("{}{}", rank, suit).red().to_string(), "laub" => format!("{}{}", rank, suit).green().to_string(), - "herz" => format!("{}{}", rank, suit).red().to_string(), "schellen" => format!("{}{}", rank, suit).yellow().to_string(), _ => format!("{}{}", rank, suit), } } + #[must_use] pub fn blank_card() -> Card { Card::new(BLANK, BLANK) } /// A valid Card is one where the Rank and Suit are not blank. /// Cards are blank when an invalid Rank or Suit are passed in. + #[must_use] pub fn is_valid(&self) -> bool { !self.rank.is_blank() && !self.suit.is_blank() } @@ -134,6 +138,7 @@ impl Named for Card { mod card_tests { use super::*; use crate::fluent::named::{GERMAN, US_ENGLISH}; + use crate::{ACE, BLANK_RANK, BLANK_SUIT, CLUBS, HEARTS, JACK, QUEEN, SPADES}; use std::cell::Cell; // region impl tests diff --git a/src/cards/decks/bridge.rs b/src/cards/decks/bridge.rs index fd1725d..ff6a8d1 100644 --- a/src/cards/decks/bridge.rs +++ b/src/cards/decks/bridge.rs @@ -3,13 +3,14 @@ use std::fmt; use crate::cards::card::Card; use crate::cards::pack::Pack; use crate::cards::pile::Pile; -use crate::cards::suit::*; +use crate::cards::suit::{Suit, CLUBS, DIAMONDS, HEARTS, SPADES}; use std::collections::HashMap; use term_table::row::Row; use term_table::table_cell::{Alignment, TableCell}; use term_table::{Table, TableStyle}; #[derive(Clone, Debug, PartialEq)] +#[allow(clippy::module_name_repetitions)] pub enum BridgeDirection { N, E, @@ -19,16 +20,13 @@ pub enum BridgeDirection { } impl BridgeDirection { + #[must_use] pub fn to(c: char) -> BridgeDirection { match c { - 'S' => BridgeDirection::S, - 's' => BridgeDirection::S, - 'N' => BridgeDirection::N, - 'n' => BridgeDirection::N, - 'E' => BridgeDirection::E, - 'e' => BridgeDirection::E, - 'W' => BridgeDirection::W, - 'w' => BridgeDirection::W, + 'S' | 's' => BridgeDirection::S, + 'N' | 'n' => BridgeDirection::N, + 'E' | 'e' => BridgeDirection::E, + 'W' | 'w' => BridgeDirection::W, _ => BridgeDirection::Unknown, } } @@ -44,9 +42,10 @@ impl BridgeDirection { } } -/// BridgeBoard is a French Deck Pack that sorts and validates the hands dealt as a part +/// `BridgeBoard` is a French Deck Pack that sorts and validates the hands dealt as a part /// of a Bridge hand. #[derive(Clone, Debug, Hash, PartialEq)] +#[allow(clippy::module_name_repetitions)] pub struct BridgeBoard { pack: Pack, pub south: Pile, @@ -57,7 +56,12 @@ pub struct BridgeBoard { impl BridgeBoard { /// Parses a Portable Bridge Notation deal string and converts it into a - /// BridgeBoard struct. + /// `BridgeBoard` struct. + /// + /// # Panics + /// + /// Will panic if an invalid PBN deal string is passed in. + #[must_use] pub fn from_pbn_deal(deal: &str) -> BridgeBoard { let (direction, pbn) = BridgeBoard::split_on_direction(deal); @@ -88,6 +92,8 @@ impl BridgeBoard { } } + #[allow(clippy::missing_panics_doc)] + #[must_use] pub fn deal() -> BridgeBoard { let mut board = BridgeBoard::default(); let mut cards = board.pack.cards().shuffle(); @@ -114,16 +120,16 @@ impl BridgeBoard { println!( "{}", BridgeBoard::compass( - BridgeBoard::compass_cell("NORTH", north), - BridgeBoard::compass_cell("WEST", west), - BridgeBoard::compass_cell("EAST", east), - BridgeBoard::compass_cell("SOUTH", south), + BridgeBoard::compass_cell("NORTH", north.as_str()), + BridgeBoard::compass_cell("WEST", west.as_str()), + BridgeBoard::compass_cell("EAST", east.as_str()), + BridgeBoard::compass_cell("SOUTH", south.as_str()), ) ); } - fn compass_cell(direction: &str, index: String) -> String { - let contents = " ".to_owned() + direction + "\n" + index.as_str(); + fn compass_cell(direction: &str, index: &str) -> String { + let contents = " ".to_owned() + direction + "\n" + index; let mut table = Table::new(); table.has_top_boarder = false; @@ -161,10 +167,12 @@ impl BridgeBoard { table.render() } + #[must_use] pub fn get_pack(&self) -> &Pack { &self.pack } + #[must_use] pub fn is_valid(&self) -> bool { let piles = &[ self.south.clone(), @@ -177,6 +185,7 @@ impl BridgeBoard { } /// Returns a Portable Bridge Notation deal string from a Bridge Board. + #[must_use] pub fn to_pbn_deal(&self) -> String { let south = BridgeBoard::hand_to_pbn_deal_segment(&self.south); let west = BridgeBoard::hand_to_pbn_deal_segment(&self.west); diff --git a/src/cards/decks/standard52.rs b/src/cards/decks/standard52.rs index 09cbf3d..d841c83 100644 --- a/src/cards/decks/standard52.rs +++ b/src/cards/decks/standard52.rs @@ -1,8 +1,8 @@ use std::fmt; use crate::cards::decks::deck_error::DeckError; -use crate::cards::rank::*; -use crate::cards::suit::*; +use crate::cards::rank::{Rank, BLANK_RANK}; +use crate::cards::suit::Suit; use crate::{Card, Pack, Pile}; #[derive(Clone, Debug, Hash, PartialEq)] @@ -12,6 +12,9 @@ pub struct Standard52 { } impl Standard52 { + /// # Errors + /// + /// Will return `DeckError::PilePackMismatch` error if `Pile` passed in isn't a `FrenchDeck`. pub fn new_from_pile(pile: Pile) -> Result { let standard52 = Standard52 { pack: Pack::french_deck(), @@ -25,6 +28,7 @@ impl Standard52 { } } + #[must_use] pub fn new_shuffled() -> Standard52 { Standard52 { pack: Pack::french_deck(), @@ -32,6 +36,9 @@ impl Standard52 { } } + /// # Errors + /// + /// Will return `DeckError::InvalidIndex` if passed in index is incomplete. pub fn from_index(card_str: &'static str) -> Result { let mut pile = Pile::default(); for index in card_str.split_whitespace() { @@ -50,24 +57,29 @@ impl Standard52 { } } + #[must_use] pub fn to_index(&self) -> String { self.deck.to_index() } + #[must_use] pub fn to_index_str(&self) -> &'static str { self.deck.to_index_str() } + #[must_use] pub fn to_symbol_index(&self) -> String { self.deck.to_symbol_index() } /// A Standard52 deck is complete if a sorted Pile of the deck is equal to it's Pack. + #[must_use] pub fn is_complete(&self) -> bool { let pile = self.deck.sort(); &pile == self.pack.cards() } + #[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)); @@ -114,6 +126,7 @@ impl fmt::Display for Standard52 { #[allow(non_snake_case)] mod standard52_tests { use super::*; + use crate::{DIAMONDS, FIVE, FOUR, SPADES, THREE, TWO}; use rstest::rstest; #[test] diff --git a/src/cards/pack.rs b/src/cards/pack.rs index 6a8a90e..455182c 100644 --- a/src/cards/pack.rs +++ b/src/cards/pack.rs @@ -34,6 +34,7 @@ impl Pack { } /// Returns true of the combined Cards from the passed in Vector match the Cards in the Pack. + #[must_use] pub fn is_complete(&self, piles: &[Pile]) -> bool { let mut pile = Pile::pile_on(piles.to_vec()); pile.sort_in_place(); @@ -41,6 +42,7 @@ impl Pack { } /// Returns a reference to the cards in the Pack. + #[must_use] pub fn cards(&self) -> &Pile { &self.cards } @@ -51,6 +53,7 @@ impl Pack { Pack::new(pile) } + #[must_use] pub fn euchre_deck() -> Pack { Pack::new(Pile::euchre_deck()) } @@ -61,26 +64,32 @@ impl Pack { Pack::new(pile) } + #[must_use] pub fn french_deck() -> Pack { Pack::new(Pile::french_deck()) } + #[must_use] pub fn french_deck_with_jokers() -> Pack { Pack::new(Pile::french_deck_with_jokers()) } + #[must_use] pub fn pinochle_deck() -> Pack { Pack::new(Pile::pinochle_deck()) } + #[must_use] pub fn short_deck() -> Pack { Pack::new(Pile::short_deck()) } + #[must_use] pub fn skat_deck() -> Pack { Pack::new(Pile::skat_deck()) } + #[must_use] pub fn spades_deck() -> Pack { Pack::new(Pile::spades_deck()) } diff --git a/src/cards/pile.rs b/src/cards/pile.rs index bc2efdc..4b955f0 100644 --- a/src/cards/pile.rs +++ b/src/cards/pile.rs @@ -7,9 +7,10 @@ use std::iter::FromIterator; use unic_langid::LanguageIdentifier; use crate::cards::card::Card; +#[allow(clippy::wildcard_imports)] use crate::cards::rank::*; -use crate::cards::suit::*; -use crate::fluent::named::*; +use crate::cards::suit::{Suit, CLUBS, DIAMONDS, HEARTS, TRUMP}; +use crate::fluent::named::{GERMAN, US_ENGLISH}; use crate::Named; /// A Pile is a sortable collection of Cards. @@ -27,11 +28,13 @@ use crate::Named; pub struct Pile(Vec); impl Pile { + #[must_use] pub fn new_from_vector(v: Vec) -> Pile { Pile(v) } /// Takes a reference to an Array of Piles and consolidates them into a single Pile of Cards. + #[must_use] pub fn pile_on(piles: Vec) -> Pile { piles.into_iter().flatten().collect() } @@ -53,7 +56,7 @@ impl Pile { { let mut pile: Vec = Vec::new(); for _ in 0..x { - pile.push(f()) + pile.push(f()); } Pile::pile_on(pile) } @@ -69,40 +72,48 @@ impl Pile { } /// Returns a simple string representation of the Cards in the Pile based upon the - /// default language local, which is US_ENGLISH. + /// default language local, which is `US_ENGLISH`. + #[must_use] pub fn to_index(&self) -> String { self.to_index_locale(&US_ENGLISH) } /// Returns a static str of the Pack's index. Mainly used for testing deserialization. /// - /// Idea from: https://stackoverflow.com/a/52367953 + /// Idea from: + #[must_use] pub fn to_index_str(&self) -> &'static str { Box::leak(self.to_index().into_boxed_str()) } + #[must_use] pub fn to_index_locale(&self, lid: &LanguageIdentifier) -> String { Pile::sig_generate_from_strings(&self.collect_index(lid)) } + #[must_use] pub fn to_symbol_index(&self) -> String { self.to_symbol_index_locale(&US_ENGLISH) } + #[must_use] pub fn to_symbol_index_locale(&self, lid: &LanguageIdentifier) -> String { Pile::sig_generate_from_strings(&self.collect_symbol_index(lid)) } + #[must_use] pub fn card_by_index(&self, index: &str) -> Option<&Card> { self.0.iter().find(|c| c.index_default() == index) } /// Returns a reference to the Vector containing all the cards. + #[must_use] pub fn cards(&self) -> &Vec { &self.0 } /// Returns a Vector of Cards matching the passed in Suit. + #[must_use] pub fn cards_by_suit(&self, suit: Suit) -> Vec { self.sort() .0 @@ -120,16 +131,19 @@ impl Pile { } /// Tests if a card is in the Pile. + #[must_use] pub fn contains(&self, card: &Card) -> bool { self.0.contains(card) } /// Tests if every element is inside the Pile. + #[must_use] pub fn contains_all(&self, pile: &Pile) -> bool { pile.cards().iter().all(|c| self.contains(c)) } /// This function is designed to demonstrate the capabilities of the library. + #[allow(clippy::similar_names)] pub fn demo(&self) { println!(" Long in English and German:"); for card in self.values() { @@ -140,7 +154,7 @@ impl Pile { println!(" {} of {} ", rankname, suitname); println!(" {} von {} ", rangname, anzugname); } - self.demo_short() + self.demo_short(); } pub fn demo_short(&self) { @@ -176,7 +190,7 @@ impl Pile { } else { let mut cards = Pile::default(); for _ in 0..x { - cards.add(self.draw_first().unwrap()); + cards.add(self.draw_first()?); } Some(cards) } @@ -196,11 +210,12 @@ impl Pile { } } + #[must_use] pub fn first(&self) -> Option<&Card> { self.0.first() } - fn fold_in(&mut self, suits: Vec, ranks: Vec) { + fn fold_in(&mut self, suits: &[Suit], ranks: &[Rank]) { for (_, suit) in suits.iter().enumerate() { for (_, rank) in ranks.iter().enumerate() { self.add(Card::new_from_structs(*rank, *suit)); @@ -208,28 +223,34 @@ impl Pile { } } + #[must_use] pub fn get(&self, index: usize) -> Option<&Card> { self.0.get(index) } + #[must_use] pub fn get_random(&self) -> Option<&Card> { self.0.choose(&mut rand::thread_rng()) } + #[must_use] pub fn is_empty(&self) -> bool { self.0.is_empty() } + #[must_use] pub fn last(&self) -> Option<&Card> { self.0.last() } + #[must_use] pub fn len(&self) -> usize { self.0.len() } - /// Takes a pile and returns a HashMap with the key as each Suit in the Pile with the values - /// as a Pile of the cards for that Suit. + /// Takes a `Pile` and returns a `HashMap` with the key as each `Suit` in the `Pile` with the values + /// as a `Pile` of the cards for that Suit. + #[must_use] pub fn map_by_suit(&self) -> HashMap { let mut mappie: HashMap = HashMap::new(); for suit in self.suits() { @@ -244,10 +265,12 @@ impl Pile { mappie } + #[must_use] pub fn position(&self, karte: &Card) -> Option { self.0.iter().position(|k| k.index == karte.index) } + #[must_use] pub fn pile_by_index(&self, indexes: &[&str]) -> Option { let mut pile = Pile::default(); for index in indexes { @@ -268,6 +291,7 @@ impl Pile { self.0 = product; } + #[must_use] pub fn ranks(&self) -> Vec { let hashset: HashSet = self.0.iter().map(|c| c.rank).collect(); let mut ranks: Vec = Vec::from_iter(hashset); @@ -277,20 +301,22 @@ impl Pile { } /// Returns a String of all of the Rank Index Characters for a Pile. + #[must_use] pub fn rank_indexes(&self) -> String { self.ranks() .iter() - .map(|c| c.to_string()) + .map(std::string::ToString::to_string) .collect::() } /// Returns a String of all of the Rank Index Characters for a Pile. /// /// TODO: There has to be an easier way to do this :-P + #[must_use] pub fn rank_indexes_with_separator(&self, separator: &'static str) -> String { self.ranks() .iter() - .map(|c| c.to_string()) + .map(std::string::ToString::to_string) .collect::() .chars() .collect::>() @@ -308,16 +334,18 @@ impl Pile { let position = self.position(card); match position { None => None, - _ => Some(self.0.remove(position.unwrap())), + _ => Some(self.0.remove(position?)), } } + #[must_use] pub fn short_index_for_suit(&self, suit: Suit) -> String { let cards = Pile::new_from_vector(self.cards_by_suit(suit)); suit.symbol().as_str().to_owned() + " " + &cards.rank_indexes_with_separator(" ") } + #[must_use] pub fn short_suit_indexes(&self) -> Vec { self.sort() .suits() @@ -328,10 +356,12 @@ impl Pile { /// Returns a String where each line is the short suit index for the Pile. /// This format is common to display hands in Bridge. + #[must_use] pub fn short_suit_indexes_to_string(&self) -> String { self.short_suit_indexes().join("\n") } + #[must_use] pub fn shuffle(&self) -> Pile { let mut shuffled = self.clone(); shuffled.shuffle_in_place(); @@ -342,6 +372,7 @@ impl Pile { self.0.shuffle(&mut thread_rng()); } + #[must_use] pub fn sort(&self) -> Pile { let mut pile = self.clone(); pile.sort_in_place(); @@ -354,6 +385,7 @@ impl Pile { } /// Returns a sorted collection of the unique Suits in a Pile. + #[must_use] pub fn suits(&self) -> Vec { let hashset: HashSet = self.0.iter().map(|c| c.suit).collect(); let mut suits: Vec = Vec::from_iter(hashset); @@ -366,22 +398,25 @@ impl Pile { self.0.iter() } + #[must_use] pub fn jokers() -> Pile { let big_joker = Card::new(BIG_JOKER, TRUMP); let little_joker = Card::new(LITTLE_JOKER, TRUMP); Pile::new_from_vector(vec![big_joker, little_joker]) } + #[must_use] pub fn canasta_base_single_deck() -> Pile { let suits = Suit::generate_french_suits(); let ranks = Rank::generate_canasta_ranks(); let mut cards: Pile = Pile::default(); - cards.fold_in(suits, ranks); + cards.fold_in(&suits, &ranks); cards.prepend(&Pile::jokers()); cards } + #[must_use] pub fn canasta_single_deck() -> Pile { let mut cards: Pile = Pile::canasta_base_single_deck(); @@ -395,31 +430,34 @@ impl Pile { fn canasta_red_threes() -> Pile { let mut three_hearts = Card::new(THREE, HEARTS); let mut three_diamonds = Card::new(THREE, DIAMONDS); - three_hearts.weight = 100001; - three_diamonds.weight = 100000; + three_hearts.weight = 100_001; + three_diamonds.weight = 100_000; Pile::new_from_vector(vec![three_hearts, three_diamonds]) } + #[must_use] pub fn euchre_deck() -> Pile { let suits = Suit::generate_french_suits(); let ranks = Rank::generate_euchre_ranks(); let mut cards: Pile = Pile::default(); - cards.fold_in(suits, ranks); + cards.fold_in(&suits, &ranks); cards.prepend(&Pile::new_from_vector(vec![Card::new(BIG_JOKER, TRUMP)])); cards } + #[must_use] pub fn french_deck() -> Pile { let suits = Suit::generate_french_suits(); let ranks = Rank::generate_french_ranks(); let mut cards: Pile = Pile::default(); - cards.fold_in(suits, ranks); + cards.fold_in(&suits, &ranks); cards } + #[must_use] pub fn french_deck_with_jokers() -> Pile { let mut pile = Pile::french_deck(); pile.prepend(&Pile::jokers()); @@ -431,32 +469,36 @@ impl Pile { let ranks = Rank::generate_pinochle_ranks(); let mut cards: Pile = Pile::default(); - cards.fold_in(suits, ranks); + cards.fold_in(&suits, &ranks); cards } + #[must_use] pub fn pinochle_deck() -> Pile { Pile::pile_up(2, Pile::pinochle_pile).sort() } + #[must_use] pub fn short_deck() -> Pile { let suits = Suit::generate_french_suits(); let ranks = Rank::generate_short_deck_ranks(); let mut cards: Pile = Pile::default(); - cards.fold_in(suits, ranks); + cards.fold_in(&suits, &ranks); cards } + #[must_use] pub fn skat_deck() -> Pile { let suits = Suit::generate_skat_suits(); let ranks = Rank::generate_skat_ranks(); let mut cards: Pile = Pile::default(); - cards.fold_in(suits, ranks); + cards.fold_in(&suits, &ranks); cards } + #[must_use] pub fn spades_deck() -> Pile { let mut deck = Pile::french_deck(); deck.remove_card(&Card::new(TWO, CLUBS)); @@ -467,6 +509,8 @@ impl Pile { deck } + #[allow(clippy::missing_panics_doc)] + #[must_use] pub fn tarot_deck() -> Pile { let arcana_suits = Suit::generate_arcana_suits(); let mut arcana_suits_enumerator = arcana_suits.iter().enumerate(); @@ -492,6 +536,7 @@ impl Pile { cards } + #[must_use] pub fn sig_generate_from_strings(strings: &[String]) -> String { strings .iter() @@ -508,7 +553,7 @@ impl Default for Pile { } } -/// Sets the to_string() function for a Pile to return the default index signature for the Pile. +/// Sets the `to_string()` function for a `Pile` to return the default index signature for the `Pile`. impl fmt::Display for Pile { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let sig = self.to_index(); @@ -539,6 +584,7 @@ impl IntoIterator for Pile { #[allow(non_snake_case)] mod card_deck_tests { use super::*; + use crate::cards::suit::{MAJOR_ARCANA, SPADES}; #[test] fn new_from_vector() { diff --git a/src/cards/rank.rs b/src/cards/rank.rs index b61612a..334cde7 100644 --- a/src/cards/rank.rs +++ b/src/cards/rank.rs @@ -70,20 +70,20 @@ pub const BLANK_RANK: &str = "_"; /// This gives you maximum flexibility. Since the value of the Ace is 1, it will be sorted /// at the and of a Suit (unless there are any Cards with negative weights). /// -/// # Rank::new() with a value string +/// # ``Rank::new()`` with a value string /// ``` /// let king = cardpack::Rank::new(cardpack::KING); /// ``` /// This sets the weight for the Rank based upon the default value as set in its fluent template /// entry. /// -/// # Rank::new_with_weight() +/// # ``Rank::new_with_weight()`` /// ``` /// let king = cardpack::Rank::new_with_weight(cardpack::QUEEN, 12); /// ``` /// Overrides the default weight for a Rank. /// -/// # Rank::from_array() +/// # ``Rank::from_array()`` /// ``` /// let ranks: Vec = cardpack::Rank::from_array(&[cardpack::ACE, cardpack::TEN,]); /// ``` @@ -98,8 +98,8 @@ pub struct Rank { } impl Rank { - /// Returns a Rank, determining its weight by the default weight value for its name set in the - /// fluent templates. For instance, if you look in `src/fluent/locales/core.ftl you will see + /// Returns a `Rank`, determining its weight by the default weight value for its name set in the + /// fluent templates. For instance, if you look in `src/fluent/locales/core.ftl` you will see /// that the default weight for an Ace is 14. This will mean that when a pile of cards is sorted /// that it will be at the top of a standard French Deck where the Ace is high. /// @@ -107,6 +107,7 @@ impl Rank { /// ``` /// let king = cardpack::Rank::new(cardpack::HERMIT); /// ``` + #[must_use] pub fn new(name: &'static str) -> Rank { let name = FluentName::new(name); Rank { @@ -122,6 +123,7 @@ impl Rank { /// ``` /// let king = cardpack::Rank::new_with_weight(cardpack::QUEEN, 12); /// ``` + #[must_use] pub fn new_with_weight(name: &'static str, weight: isize) -> Rank { Rank { weight, @@ -137,10 +139,11 @@ impl Rank { /// ``` /// Returns a Vector of Ranks with their weights determined by the order they're passed in, high to /// low. This facilitates the easy creation of custom decks, such as for pinochle. + #[must_use] pub fn from_array(s: &[&'static str]) -> Vec { let mut v: Vec = Vec::new(); - #[allow(clippy::into_iter_on_ref)] + #[allow(clippy::cast_possible_wrap, clippy::into_iter_on_ref)] for (i, &elem) in s.into_iter().enumerate() { let weight = (s.len() + 1) - i; v.push(Rank::new_with_weight(elem, weight as isize)); @@ -149,6 +152,7 @@ impl Rank { } /// Returns a Rank entity based on its index string. + #[must_use] pub fn from_french_deck_index(index: &'static str) -> Rank { match index { "JB" => Rank::new(BIG_JOKER), @@ -157,9 +161,7 @@ impl Rank { "K" => Rank::new(KING), "Q" => Rank::new(QUEEN), "J" => Rank::new(JACK), - "T" => Rank::new(TEN), - "10" => Rank::new(TEN), - "0" => Rank::new(TEN), + "T" | "0" | "10" => Rank::new(TEN), "9" => Rank::new(NINE), "8" => Rank::new(EIGHT), "7" => Rank::new(SEVEN), @@ -172,26 +174,31 @@ impl Rank { } } + #[must_use] pub fn generate_canasta_ranks() -> Vec { Rank::from_array(&[ TWO, ACE, KING, QUEEN, JACK, TEN, NINE, EIGHT, SEVEN, SIX, FIVE, FOUR, THREE, ]) } + #[must_use] pub fn generate_euchre_ranks() -> Vec { Rank::from_array(&[ACE, KING, QUEEN, JACK, TEN, NINE]) } + #[must_use] pub fn generate_french_ranks() -> Vec { Rank::from_array(&[ ACE, KING, QUEEN, JACK, TEN, NINE, EIGHT, SEVEN, SIX, FIVE, FOUR, THREE, TWO, ]) } + #[must_use] pub fn generate_pinochle_ranks() -> Vec { Rank::from_array(&[ACE, TEN, KING, QUEEN, JACK, NINE]) } + #[must_use] pub fn generate_major_arcana_ranks() -> Vec { Rank::from_array(&[ FOOL, MAGICIAN, PRIESTESS, EMPRESS, EMPEROR, HIEROPHANT, LOVERS, CHARIOT, STRENGTH, @@ -200,20 +207,24 @@ impl Rank { ]) } + #[must_use] pub fn generate_minor_arcana_ranks() -> Vec { Rank::from_array(&[ KING, QUEEN, KNIGHT, PAGE, TEN, NINE, EIGHT, SEVEN, SIX, FIVE, FOUR, THREE, TWO, ACE, ]) } + #[must_use] pub fn generate_short_deck_ranks() -> Vec { Rank::from_array(&[ACE, KING, QUEEN, JACK, TEN, NINE, EIGHT, SEVEN, SIX]) } + #[must_use] pub fn generate_skat_ranks() -> Vec { Rank::from_array(&[DAUS, KING, OBER, UNTER, TEN, NINE, EIGHT, SEVEN]) } + #[must_use] pub fn is_blank(&self) -> bool { self.name.name() == BLANK_RANK } diff --git a/src/cards/suit.rs b/src/cards/suit.rs index a85431f..2d59136 100644 --- a/src/cards/suit.rs +++ b/src/cards/suit.rs @@ -34,6 +34,7 @@ pub struct Suit { } impl Suit { + #[must_use] pub fn new(name_str: &'static str) -> Suit { let name = FluentName::new(name_str); Suit { @@ -42,6 +43,7 @@ impl Suit { } } + #[must_use] pub fn new_with_weight(name: &'static str, weight: isize) -> Suit { Suit { weight, @@ -49,14 +51,17 @@ impl Suit { } } + #[must_use] pub fn is_blank(&self) -> bool { self.name.name() == BLANK_SUIT } + #[must_use] pub fn symbol(&self) -> String { self.name.fluent_value(FLUENT_SYMBOL_SECTION, &US_ENGLISH) } + #[allow(clippy::cast_possible_wrap)] fn top_down_value(len: usize, i: usize) -> isize { (len - i) as isize } @@ -77,30 +82,29 @@ impl Suit { } /// Returns a Suit from its symbol string. + #[must_use] pub fn from_french_deck_index(symbol: char) -> Suit { match symbol { - '♠' => Suit::new(SPADES), - 'S' => Suit::new(SPADES), - '♥' => Suit::new(HEARTS), - 'H' => Suit::new(HEARTS), - '♦' => Suit::new(DIAMONDS), - 'D' => Suit::new(DIAMONDS), - '♣' => Suit::new(CLUBS), - 'C' => Suit::new(CLUBS), - '🃟' => Suit::new(TRUMP), - 'T' => Suit::new(TRUMP), + '♠' | 'S' => Suit::new(SPADES), + '♥' | 'H' => Suit::new(HEARTS), + '♦' | 'D' => Suit::new(DIAMONDS), + '♣' | 'C' => Suit::new(CLUBS), + '🃟' | 'T' => Suit::new(TRUMP), _ => Suit::new(BLANK_SUIT), } } + #[must_use] pub fn generate_french_suits() -> Vec { Suit::from_array(&[SPADES, HEARTS, DIAMONDS, CLUBS]) } + #[must_use] pub fn generate_arcana_suits() -> Vec { Suit::from_array(&[MAJOR_ARCANA, WANDS, CUPS, SWORDS, PENTACLES]) } + #[must_use] pub fn generate_skat_suits() -> Vec { Suit::from_array(&[EICHEL, LAUB, HERZ, SHELLEN]) } diff --git a/src/fluent/fluent_name.rs b/src/fluent/fluent_name.rs index 39ce214..9873e27 100644 --- a/src/fluent/fluent_name.rs +++ b/src/fluent/fluent_name.rs @@ -4,17 +4,18 @@ use crate::Named; const BLANK: &str = "blank"; -/// FluentName is the primary implementation of the Named trait. +/// `FluentName` is the primary implementation of the Named trait. /// -/// FluentName represents the fluent template key for a card entity such as a Suit or Rank, +/// `FluentName` represents the fluent template key for a card entity such as a Suit or Rank, /// which in turn determines its long name in any represented language, the short letter /// used to display an index, and the default weight for the if it is instantiated via -/// `::new()`. A FluentName must have a corresponding entries in the fluent templates for +/// `::new()`. A `FluentName` must have a corresponding entries in the fluent templates for /// weight, long, and index. #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct FluentName(&'static str); impl FluentName { + #[must_use] pub fn new(name: &'static str) -> FluentName { if name.trim().is_empty() { FluentName(BLANK) diff --git a/src/fluent/mod.rs b/src/fluent/mod.rs index 3cac860..4a13b92 100644 --- a/src/fluent/mod.rs +++ b/src/fluent/mod.rs @@ -1,2 +1,3 @@ +#![allow(clippy::module_name_repetitions)] pub mod fluent_name; pub mod named; diff --git a/src/fluent/named.rs b/src/fluent/named.rs index b582e76..2c9a42e 100644 --- a/src/fluent/named.rs +++ b/src/fluent/named.rs @@ -45,7 +45,7 @@ pub trait Named { self.fluent_value(FLUENT_INDEX_SECTION, lid) } - /// Returns the default, US_ENGLISH value of the names' index value in the fluent templates. + /// Returns the default, `US_ENGLISH` value of the names' index value in the fluent templates. /// /// ## Usage /// ``` @@ -75,7 +75,7 @@ pub trait Named { self.fluent_value(FLUENT_LONG_SECTION, lid) } - /// Returns the default, US_ENGLISH value of the names' long value in the fluent templates. + /// Returns the default, `US_ENGLISH` value of the names' long value in the fluent templates. fn long_default(&self) -> String { self.long(&US_ENGLISH) } diff --git a/src/lib.rs b/src/lib.rs index 3a6b364..8ab5c23 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +#![warn(clippy::pedantic)] + extern crate rand; pub mod cards;