1use std::{
2 fmt::{Debug, Display, Formatter},
3 str::FromStr,
4 sync::atomic::{AtomicUsize, Ordering},
5};
6
7use enum_iterator::{Sequence, all};
8use serde::{Deserialize, Serialize, de::Visitor};
9
10pub type Chips = f64;
11pub type Mult = f64;
12
13#[derive(Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
21pub struct Round {
22 pub cards_played: Vec<Card>,
23
24 #[serde(default)]
25 pub cards_held_in_hand: Vec<Card>,
26
27 #[serde(default)]
28 pub jokers: Vec<JokerCard>,
29}
30
31#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
51pub struct Card {
52 pub rank: Rank,
53 pub suit: Suit,
54 pub enhancement: Option<Enhancement>,
55 pub edition: Option<Edition>,
56 #[doc(hidden)]
57 unique_index: usize,
58 }
60
61#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Sequence)]
76pub enum Rank {
77 Two,
78 Three,
79 Four,
80 Five,
81 Six,
82 Seven,
83 Eight,
84 Nine,
85 Ten,
86 Jack,
87 Queen,
88 King,
89 Ace,
90}
91
92#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Sequence)]
94pub enum Suit {
95 Spades,
96 Hearts,
97 Clubs,
98 Diamonds,
99}
100
101#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Sequence)]
107pub enum SuitColor {
108 Black,
109 Red,
110}
111
112#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
127pub struct JokerCard {
128 pub joker: Joker,
129 pub edition: Option<Edition>,
130 #[doc(hidden)]
131 unique_index: usize,
132}
133
134#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Sequence)]
138pub enum Joker {
139 Joker,
144
145 JollyJoker,
149
150 ZanyJoker,
154
155 MadJoker,
159
160 CrazyJoker,
164
165 DrollJoker,
169
170 SlyJoker,
174
175 WilyJoker,
179
180 CleverJoker,
184
185 DeviousJoker,
189
190 CraftyJoker,
194
195 AbstractJoker,
200
201 RaisedFist,
205
206 Blackboard,
210
211 Baron,
215
216 GreedyJoker,
221
222 LustyJoker,
226
227 WrathfulJoker,
231
232 GluttonousJoker,
236
237 Fibonacci,
241
242 ScaryFace,
246
247 EvenSteven,
253
254 OddTodd,
260
261 Photograph,
265
266 SmileyFace,
270
271 FlowerPot,
276
277 FourFingers,
291
292 Shortcut,
298
299 Mime,
303
304 Pareidolia,
308
309 Splash,
313
314 SockAndBuskin,
318
319 SmearedJoker,
324
325 Blueprint,
329 }
331
332#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Sequence)]
334pub enum Enhancement {
335 Bonus,
337
338 Mult,
340
341 Wild,
343
344 Glass,
347
348 Steel,
350 }
352
353#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Sequence)]
355pub enum Edition {
356 Foil,
358
359 Holographic,
361
362 Polychrome,
364 }
366
367#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Sequence)]
415pub enum PokerHand {
416 HighCard = 0,
421
422 Pair,
426
427 TwoPair,
432
433 ThreeOfAKind,
437
438 Straight,
444
445 Flush,
449
450 FullHouse,
455
456 FourOfAKind,
460
461 StraightFlush,
465
466 FiveOfAKind,
473
474 FlushHouse,
481
482 FlushFive,
488}
489
490impl Card {
491 pub fn new(
492 rank: Rank,
493 suit: Suit,
494 enhancement: Option<Enhancement>,
495 edition: Option<Edition>,
496 ) -> Self {
497 static UNIQUE_INDEX: AtomicUsize = AtomicUsize::new(0);
498
499 Self {
500 rank,
501 suit,
502 enhancement,
503 edition,
504 unique_index: UNIQUE_INDEX.fetch_add(1, Ordering::SeqCst),
505 }
506 }
507}
508
509impl Serialize for Card {
510 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
511 where
512 S: serde::Serializer,
513 {
514 serializer.serialize_str(&self.to_string())
515 }
516}
517
518impl<'de> Deserialize<'de> for Card {
519 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
520 where
521 D: serde::Deserializer<'de>,
522 {
523 struct CardVisitor;
524 impl Visitor<'_> for CardVisitor {
525 type Value = Card;
526
527 fn expecting(&self, f: &mut Formatter) -> std::fmt::Result {
528 write!(f, "Card")
529 }
530
531 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
532 where
533 E: serde::de::Error,
534 {
535 v.parse().map_err(|err| serde::de::Error::custom(err))
536 }
537 }
538
539 deserializer.deserialize_str(CardVisitor)
540 }
541}
542
543impl Serialize for JokerCard {
544 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
545 where
546 S: serde::Serializer,
547 {
548 serializer.serialize_str(&self.to_string())
549 }
550}
551
552impl<'de> Deserialize<'de> for JokerCard {
553 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
554 where
555 D: serde::Deserializer<'de>,
556 {
557 struct CardVisitor;
558 impl Visitor<'_> for CardVisitor {
559 type Value = JokerCard;
560
561 fn expecting(&self, f: &mut Formatter) -> std::fmt::Result {
562 write!(f, "JokerCard")
563 }
564
565 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
566 where
567 E: serde::de::Error,
568 {
569 v.parse().map_err(|err| serde::de::Error::custom(err))
570 }
571 }
572
573 deserializer.deserialize_str(CardVisitor)
574 }
575}
576
577impl Debug for Card {
578 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
579 Display::fmt(self, f)
580 }
581}
582
583impl Display for Card {
584 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
585 let Self { rank, suit, .. } = self;
586
587 write!(f, "{rank}{suit}")?;
588
589 if let Some(enhancement) = self.enhancement {
590 write!(f, " {enhancement}")?;
591 }
592
593 if let Some(edition) = self.edition {
594 write!(f, " {edition}")?;
595 }
596
597 Ok(())
598 }
599}
600
601impl FromStr for Card {
602 type Err = String;
603
604 fn from_str(s: &str) -> Result<Self, Self::Err> {
605 let mut parts = s.split_ascii_whitespace();
606
607 let rank_suit = parts.next().ok_or("Cannot parse empty string")?;
608
609 let enhancement_or_edition_str = parts.next();
610 let edition_str = parts.next();
611
612 if let Some(invalid) = parts.next() {
613 return Err(format!("Card `{s}` contains too much data: `{invalid}`"));
614 }
615
616 let mut reversed = rank_suit.chars().rev();
618 let suit_str = reversed
619 .next()
620 .ok_or_else(|| format!("Card `{s}` missing rank / suit"))?
621 .to_string();
622 let rank_str: String = reversed.rev().collect();
623
624 let rank = rank_str
625 .parse()
626 .map_err(|err| format!("Card `{s}` has invalid rank: {err}"))?;
627
628 let suit = suit_str
629 .parse()
630 .map_err(|err| format!("Card `{s}` has invalid suit: {err}"))?;
631
632 let (enhancement, edition) = {
633 let edition = edition_str
634 .map(|edition| edition.parse())
635 .transpose()
636 .map_err(|err| format!("Card `{s}` has invalid edition: {err}"))?;
637
638 if edition.is_some() {
639 let enhancement = enhancement_or_edition_str
640 .map(|enhancement| enhancement.parse())
641 .transpose()
642 .map_err(|err| format!("Card `{s}` has invalid enhancement: {err}"))?;
643
644 (enhancement, edition)
645 } else if let Some(enhancement_or_edition_str) = enhancement_or_edition_str {
646 let enhancement = enhancement_or_edition_str.parse::<Enhancement>();
647 let edition = enhancement_or_edition_str.parse::<Edition>();
648
649 match (enhancement, edition) {
650 (Ok(_), Ok(_)) => unreachable!("Enhancements and editions have distinct names"),
651 (Ok(enhancement), _) => (Some(enhancement), None),
652 (Err(_), Ok(edition)) => (None, Some(edition)),
653 (Err(_), Err(_)) => {
654 return Err(format!(
655 "Card `{s}` has invalid enhancement / edition: {enhancement_or_edition_str}"
656 ));
657 }
658 }
659 } else {
660 (None, None)
661 }
662 };
663
664 Ok(Card::new(rank, suit, enhancement, edition))
665 }
666}
667
668impl JokerCard {
669 pub fn new(joker: Joker, edition: Option<Edition>) -> Self {
670 static UNIQUE_INDEX: AtomicUsize = AtomicUsize::new(0);
671
672 Self {
673 joker,
674 edition,
675 unique_index: UNIQUE_INDEX.fetch_add(1, Ordering::SeqCst),
676 }
677 }
678}
679
680impl Debug for JokerCard {
681 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
682 Display::fmt(self, f)
683 }
684}
685
686impl Display for JokerCard {
687 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
688 let joker = self.joker;
689
690 write!(f, "{joker}")?;
691
692 if let Some(edition) = self.edition {
693 write!(f, " {edition}")?;
694 }
695
696 Ok(())
697 }
698}
699
700impl FromStr for JokerCard {
701 type Err = String;
702
703 fn from_str(s: &str) -> Result<Self, Self::Err> {
704 let mut joker_str = s;
705 let mut edition = None;
706
707 for possible_edition in all::<Edition>() {
708 if let Some(leftover) = s.strip_suffix(&possible_edition.to_string()) {
709 joker_str = leftover.trim();
710 edition = Some(possible_edition);
711
712 break;
713 }
714 }
715
716 let joker = joker_str
717 .parse()
718 .map_err(|err| format!("Invalid JokerCard `{s}`: {err}"))?;
719
720 Ok(JokerCard::new(joker, edition))
721 }
722}
723
724impl Debug for Joker {
725 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
726 Display::fmt(self, f)
727 }
728}
729
730impl Display for Joker {
731 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
732 use crate::Joker::*;
733
734 #[rustfmt::skip]
735 let name = match self {
736 Joker => "Joker",
737 JollyJoker => "Jolly Joker",
738 ZanyJoker => "Zany Joker",
739 MadJoker => "Mad Joker",
740 CrazyJoker => "Crazy Joker",
741 DrollJoker => "Droll Joker",
742 SlyJoker => "Sly Joker",
743 WilyJoker => "Wily Joker",
744 CleverJoker => "Clever Joker",
745 DeviousJoker => "Devious Joker",
746 CraftyJoker => "Crafty Joker",
747 AbstractJoker => "Abstract Joker",
748 RaisedFist => "Raised Fist",
749 Blackboard => "Blackboard",
750 Baron => "Baron",
751 GreedyJoker => "Greedy Joker",
752 LustyJoker => "Lusty Joker",
753 WrathfulJoker => "Wrathful Joker",
754 GluttonousJoker => "Gluttonous Joker",
755 Fibonacci => "Fibonacci",
756 ScaryFace => "Scary Face",
757 EvenSteven => "Even Steven",
758 OddTodd => "Odd Todd",
759 Photograph => "Photograph",
760 SmileyFace => "Smiley Face",
761 FlowerPot => "Flower Pot",
762 FourFingers => "Four Fingers",
763 Shortcut => "Shortcut",
764 Mime => "Mime",
765 Pareidolia => "Pareidolia",
766 Splash => "Splash",
767 SockAndBuskin => "Sock And Buskin",
768 SmearedJoker => "Smeared Joker",
769 Blueprint => "Blueprint",
770 };
771
772 write!(f, "{name}")
773 }
774}
775
776impl FromStr for Joker {
777 type Err = String;
778
779 fn from_str(s: &str) -> Result<Self, Self::Err> {
780 use crate::Joker::*;
781
782 #[rustfmt::skip]
783 let value = match s {
784 "Joker" => Joker,
785 "Jolly Joker" => JollyJoker,
786 "Zany Joker" => ZanyJoker,
787 "Mad Joker" => MadJoker,
788 "Crazy Joker" => CrazyJoker,
789 "Droll Joker" => DrollJoker,
790 "Sly Joker" => SlyJoker,
791 "Wily Joker" => WilyJoker,
792 "Clever Joker" => CleverJoker,
793 "Devious Joker" => DeviousJoker,
794 "Crafty Joker" => CraftyJoker,
795 "Abstract Joker" => AbstractJoker,
796 "Raised Fist" => RaisedFist,
797 "Blackboard" => Blackboard,
798 "Baron" => Baron,
799 "Greedy Joker" => GreedyJoker,
800 "Lusty Joker" => LustyJoker,
801 "Wrathful Joker" => WrathfulJoker,
802 "Gluttonous Joker" => GluttonousJoker,
803 "Fibonacci" => Fibonacci,
804 "Scary Face" => ScaryFace,
805 "Even Steven" => EvenSteven,
806 "Odd Todd" => OddTodd,
807 "Photograph" => Photograph,
808 "Smiley Face" => SmileyFace,
809 "Flower Pot" => FlowerPot,
810 "Four Fingers" => FourFingers,
811 "Shortcut" => Shortcut,
812 "Mime" => Mime,
813 "Pareidolia" => Pareidolia,
814 "Splash" => Splash,
815 "Sock And Buskin" => SockAndBuskin,
816 "Smeared Joker" => SmearedJoker,
817 "Blueprint" => Blueprint,
818 _ => return Err(format!("Invalid Joker: `{s}`")),
819 };
820
821 Ok(value)
822 }
823}
824
825impl Debug for Rank {
826 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
827 Display::fmt(self, f)
828 }
829}
830
831impl Display for Rank {
832 #[rustfmt::skip]
833 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
834 use Rank::*;
835
836 write!(f, "{}", match *self {
837 Two => "2",
838 Three => "3",
839 Four => "4",
840 Five => "5",
841 Six => "6",
842 Seven => "7",
843 Eight => "8",
844 Nine => "9",
845 Ten => "10",
846 Jack => "J",
847 Queen => "Q",
848 King => "K",
849 Ace => "A",
850 })
851 }
852}
853
854impl FromStr for Rank {
855 type Err = String;
856
857 fn from_str(s: &str) -> Result<Self, Self::Err> {
858 use Rank::*;
859
860 #[rustfmt::skip]
861 let value = match s {
862 "2" => Two,
863 "3" => Three,
864 "4" => Four,
865 "5" => Five,
866 "6" => Six,
867 "7" => Seven,
868 "8" => Eight,
869 "9" => Nine,
870 "10" => Ten,
871 "J" => Jack,
872 "Q" => Queen,
873 "K" => King,
874 "A" => Ace,
875 _ => return Err(format!("Invalid Rank: `{s}`")),
876 };
877
878 Ok(value)
879 }
880}
881
882impl Debug for Suit {
883 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
884 Display::fmt(self, f)
885 }
886}
887
888impl Display for Suit {
889 #[rustfmt::skip]
890 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
891 use Suit::*;
892
893 write!(f, "{}", match *self {
894 Spades => "♠",
895 Hearts => "♥",
896 Clubs => "♣",
897 Diamonds => "♦",
898 })
899 }
900}
901
902impl FromStr for Suit {
903 type Err = String;
904
905 fn from_str(s: &str) -> Result<Self, Self::Err> {
906 use Suit::*;
907
908 let value = match s {
909 "♠" => Spades,
910 "♥" => Hearts,
911 "♣" => Clubs,
912 "♦" => Diamonds,
913 _ => return Err(format!("Invalid Suit: `{s}`")),
914 };
915
916 Ok(value)
917 }
918}
919
920impl Debug for Enhancement {
921 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
922 Display::fmt(self, f)
923 }
924}
925
926impl Display for Enhancement {
927 #[rustfmt::skip]
928 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
929 use Enhancement::*;
930
931 write!(f, "{}", match *self {
932 Bonus => "Bonus",
933 Mult => "Mult",
934 Wild => "Wild",
935 Glass => "Glass",
936 Steel => "Steel",
937 })
938 }
939}
940
941impl FromStr for Enhancement {
942 type Err = String;
943
944 fn from_str(s: &str) -> Result<Self, Self::Err> {
945 use Enhancement::*;
946
947 #[rustfmt::skip]
948 let value = match s {
949 "Bonus" => Bonus,
950 "Mult" => Mult,
951 "Wild" => Wild,
952 "Glass" => Glass,
953 "Steel" => Steel,
954 _ => return Err(format!("Invalid Enhancement: `{s}`")),
955 };
956
957 Ok(value)
958 }
959}
960
961impl Debug for Edition {
962 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
963 Display::fmt(self, f)
964 }
965}
966
967impl Display for Edition {
968 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
969 use Edition::*;
970
971 write!(
972 f,
973 "{}",
974 match *self {
975 Foil => "Foil",
976 Holographic => "Holographic",
977 Polychrome => "Polychrome",
978 }
979 )
980 }
981}
982
983impl FromStr for Edition {
984 type Err = String;
985
986 fn from_str(s: &str) -> Result<Self, Self::Err> {
987 use Edition::*;
988
989 let value = match s {
990 "Foil" => Foil,
991 "Holographic" => Holographic,
992 "Polychrome" => Polychrome,
993 _ => return Err(format!("Invalid Edition: `{s}`")),
994 };
995
996 Ok(value)
997 }
998}
999
1000impl Debug for PokerHand {
1001 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1002 Display::fmt(&self, f)
1003 }
1004}
1005
1006impl Display for PokerHand {
1007 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1008 use PokerHand::*;
1009
1010 write!(
1011 f,
1012 "{}",
1013 match self {
1014 HighCard => "High Card",
1015 Pair => "Pair",
1016 TwoPair => "Two Pair",
1017 ThreeOfAKind => "Three Of A Kind",
1018 Straight => "Straight",
1019 Flush => "Flush",
1020 FullHouse => "Full House",
1021 FourOfAKind => "Four Of A Kind",
1022 StraightFlush => "Straight Flush",
1023 FiveOfAKind => "Five Of A Kind",
1024 FlushHouse => "Flush House",
1025 FlushFive => "Flush Five",
1026 }
1027 )
1028 }
1029}
1030
1031impl Rank {
1032 pub fn rank_value(&self) -> Chips {
1033 use Rank::*;
1034
1035 let value = match *self {
1036 Two => 2,
1037 Three => 3,
1038 Four => 4,
1039 Five => 5,
1040 Six => 6,
1041 Seven => 7,
1042 Eight => 8,
1043 Nine => 9,
1044 Ten | Jack | Queen | King => 10,
1045 Ace => 11,
1046 };
1047
1048 value.into()
1049 }
1050
1051 pub fn is_face(&self) -> bool {
1053 use Rank::*;
1054 matches!(self, Jack | Queen | King)
1055 }
1056}
1057
1058impl PokerHand {
1059 pub fn hand_value(&self) -> (Chips, Mult) {
1060 use PokerHand::*;
1061
1062 #[rustfmt::skip]
1063 let (chips, mult) = match *self {
1064 HighCard => (5, 1),
1065 Pair => (10, 2),
1066 TwoPair => (20, 2),
1067 ThreeOfAKind => (30, 3),
1068 Straight => (30, 4),
1069 Flush => (35, 4),
1070 FullHouse => (40, 4),
1071 FourOfAKind => (60, 7),
1072 StraightFlush => (100, 8),
1073 FiveOfAKind => (120, 12),
1074 FlushHouse => (140, 14),
1075 FlushFive => (160, 16),
1076 };
1077
1078 (chips.into(), mult.into())
1079 }
1080}
1081
1082impl Suit {
1083 pub fn color(&self) -> SuitColor {
1084 use Suit::*;
1085 use SuitColor::*;
1086
1087 match *self {
1088 Spades | Clubs => Black,
1089 Hearts | Diamonds => Red,
1090 }
1091 }
1092
1093 pub fn other_suit_of_same_color(&self) -> Suit {
1094 use Suit::*;
1095
1096 match *self {
1097 Spades => Clubs,
1098 Hearts => Diamonds,
1099 Clubs => Spades,
1100 Diamonds => Hearts,
1101 }
1102 }
1103}
1104
1105impl SuitColor {
1106 pub fn other_color(&self) -> SuitColor {
1107 use SuitColor::*;
1108
1109 match *self {
1110 Black => Red,
1111 Red => Black,
1112 }
1113 }
1114}
1115
1116#[cfg(test)]
1117mod tests {
1118 use crate::*;
1119 use enum_iterator::all;
1120
1121 #[test]
1122 fn jokers_to_string_parse() {
1123 for joker in all::<Joker>() {
1124 assert_eq!(Ok(joker), joker.to_string().parse())
1125 }
1126 }
1127
1128 #[test]
1129 fn suits_to_string_parse() {
1130 for suit in all::<Suit>() {
1131 assert_eq!(Ok(suit), suit.to_string().parse())
1132 }
1133 }
1134
1135 #[test]
1136 fn ranks_to_string_parse() {
1137 for rank in all::<Rank>() {
1138 assert_eq!(Ok(rank), rank.to_string().parse())
1139 }
1140 }
1141
1142 #[test]
1143 fn rank_is_face() {
1144 use Rank::*;
1145
1146 let face_cards = [Jack, Queen, King];
1148 for card in face_cards {
1149 assert!(card.is_face(), "{card:?} should be a face card");
1150 }
1151
1152 let non_face_cards = [Ace, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten];
1154 for card in non_face_cards {
1155 assert!(!card.is_face(), "{card:?} should not be a face card");
1156 }
1157 }
1158
1159 #[test]
1160 fn enhancements_to_string_parse() {
1161 for enhancement in all::<Enhancement>() {
1162 assert_eq!(Ok(enhancement), enhancement.to_string().parse())
1163 }
1164 }
1165
1166 #[test]
1167 fn editions_to_string_parse() {
1168 for edition in all::<Edition>() {
1169 assert_eq!(Ok(edition), edition.to_string().parse())
1170 }
1171 }
1172
1173 #[test]
1174 fn serialize_round() {
1175 use crate::Joker::*;
1176 use Edition::*;
1177 use Enhancement::*;
1178 use Rank::*;
1179 use Suit::*;
1180
1181 #[rustfmt::skip]
1182 let round = Round {
1183 cards_played: vec![
1184 Card::new(Ace, Hearts, None, None),
1185 Card::new(Queen, Clubs, Some(Bonus), None),
1186 Card::new(Ten, Spades, None, Some(Holographic)),
1187 Card::new(Seven, Diamonds, Some(Glass), Some(Polychrome)),
1188 ],
1189 cards_held_in_hand: vec![
1190 Card::new(Ace, Clubs, None, None),
1191 ],
1192 jokers: vec![
1193 JokerCard::new(Joker, None),
1194 JokerCard::new(Blueprint, Some(Foil)),
1195 JokerCard::new(Photograph, Some(Holographic)),
1196 JokerCard::new(SockAndBuskin, Some(Polychrome)),
1197 JokerCard::new(FlowerPot, Some(Polychrome)),
1198 ],
1199 };
1200
1201 let serialized = serde_yaml::to_string(&round);
1202 assert!(serialized.is_ok(), "{serialized:?}");
1203 let serialized = serialized.unwrap();
1204
1205 let expected = r#"
1206cards_played:
1207- A♥
1208- Q♣ Bonus
1209- 10♠ Holographic
1210- 7♦ Glass Polychrome
1211cards_held_in_hand:
1212- A♣
1213jokers:
1214- Joker
1215- Blueprint Foil
1216- Photograph Holographic
1217- Sock And Buskin Polychrome
1218- Flower Pot Polychrome
1219"#
1220 .trim_start();
1221
1222 assert_eq!(serialized, expected);
1223 }
1224
1225 #[test]
1226 fn deserialize_round() {
1227 use crate::Joker::*;
1228 use Edition::*;
1229 use Enhancement::*;
1230 use Rank::*;
1231 use Suit::*;
1232
1233 let serialized = r#"
1234cards_played:
1235- A♥
1236- Q♣ Bonus
1237- 10♠ Holographic
1238- 7♦ Glass Polychrome
1239cards_held_in_hand:
1240- A♣
1241jokers:
1242- Joker
1243- Blueprint Foil
1244- Photograph Holographic
1245- Sock And Buskin Polychrome
1246- Flower Pot Polychrome
1247"#
1248 .trim_start();
1249
1250 #[rustfmt::skip]
1251 #[allow(unused)]
1252 let expected = Round {
1253 cards_played: vec![
1254 Card::new(Ace, Hearts, None, None),
1255 Card::new(Queen, Clubs, Some(Bonus), None),
1256 Card::new(Ten, Spades, None, Some(Holographic)),
1257 Card::new(Seven, Diamonds, Some(Glass), Some(Polychrome)),
1258 ],
1259 cards_held_in_hand: vec![
1260 Card::new(Ace, Clubs, None, None),
1261 ],
1262 jokers: vec![
1263 JokerCard::new(Joker, None),
1264 JokerCard::new(Blueprint, Some(Foil)),
1265 JokerCard::new(Photograph, Some(Holographic)),
1266 JokerCard::new(SockAndBuskin, Some(Polychrome)),
1267 JokerCard::new(FlowerPot, Some(Polychrome)),
1268 ],
1269 };
1270
1271 let deserialized = serde_yaml::from_str::<Round>(serialized);
1272 assert!(deserialized.is_ok(), "{deserialized:?}");
1273 let deserialized = deserialized.unwrap();
1274
1275 let reserialized = serde_yaml::to_string(&deserialized);
1276 assert!(reserialized.is_ok(), "{reserialized:?}");
1277 let reserialized = reserialized.unwrap();
1278
1279 assert_eq!(serialized, reserialized);
1280 }
1281
1282 #[test]
1283 fn test_card_serialization() {
1284 let card = Card::new(
1285 Rank::Ace,
1286 Suit::Hearts,
1287 Some(Enhancement::Bonus),
1288 Some(Edition::Foil),
1289 );
1290 let serialized = serde_yaml::to_string(&card).unwrap();
1291 assert_eq!(serialized, "A♥ Bonus Foil\n");
1292 }
1293
1294 #[test]
1295 fn test_card_deserialization() {
1296 let serialized = "A♥ Bonus Foil";
1297 let card: Card = serde_yaml::from_str(serialized).unwrap();
1298 let expected = Card::new(
1299 Rank::Ace,
1300 Suit::Hearts,
1301 Some(Enhancement::Bonus),
1302 Some(Edition::Foil),
1303 );
1304 assert_eq!(
1305 format!("{card:?}").trim(),
1306 format!("{expected:?}").trim(),
1307 "Deserialization failed: expected {:?}, got {:?}",
1308 expected,
1309 card
1310 );
1311 }
1312
1313 #[test]
1314 fn test_joker_card_serialization() {
1315 let joker_card = JokerCard::new(Joker::JollyJoker, Some(Edition::Holographic));
1316 let serialized = serde_yaml::to_string(&joker_card).unwrap();
1317 assert_eq!(serialized, "Jolly Joker Holographic\n");
1318 }
1319
1320 #[test]
1321 fn test_joker_card_deserialization() {
1322 let serialized = "Jolly Joker Holographic";
1323 let joker_card: JokerCard = serde_yaml::from_str(serialized).unwrap();
1324 let expected = JokerCard::new(Joker::JollyJoker, Some(Edition::Holographic));
1325 assert_eq!(
1326 format!("{joker_card:?}").trim(),
1327 format!("{expected:?}").trim(),
1328 "Deserialization failed: expected {:?}, got {:?}",
1329 expected,
1330 joker_card
1331 );
1332 }
1333
1334 #[test]
1335 fn test_round_trip_card() {
1336 let card = Card::new(Rank::King, Suit::Diamonds, None, None);
1337 let serialized = serde_yaml::to_string(&card).unwrap();
1338 let deserialized: Card = serde_yaml::from_str(&serialized).unwrap();
1339 assert_eq!(
1340 format!("{card:?}").trim(),
1341 format!("{deserialized:?}").trim(),
1342 "Round-trip failed: expected {:?}, got {:?}",
1343 card,
1344 deserialized
1345 );
1346 }
1347
1348 #[test]
1349 fn test_round_trip_joker_card() {
1350 let joker_card = JokerCard::new(Joker::CraftyJoker, None);
1351 let serialized = serde_yaml::to_string(&joker_card).unwrap();
1352 let deserialized: JokerCard = serde_yaml::from_str(&serialized).unwrap();
1353 assert_eq!(
1354 format!("{joker_card:?}").trim(),
1355 format!("{deserialized:?}").trim(),
1356 "Round-trip failed: expected {:?}, got {:?}",
1357 joker_card,
1358 deserialized
1359 );
1360 }
1361
1362 #[test]
1363 fn test_invalid_card_deserialization() {
1364 let serialized = "Invalid Card";
1365 let result: Result<Card, _> = serde_yaml::from_str(serialized);
1366 assert!(result.is_err());
1367 }
1368
1369 #[test]
1370 fn test_invalid_joker_card_deserialization() {
1371 let serialized = "Invalid JokerCard";
1372 let result: Result<JokerCard, _> = serde_yaml::from_str(serialized);
1373 assert!(result.is_err());
1374 }
1375
1376 #[test]
1377 fn test_card_serialization_various_cases() {
1378 let cases = vec![
1379 (Rank::Two, Suit::Hearts, None, None, "2♥\n"),
1380 (
1381 Rank::Three,
1382 Suit::Clubs,
1383 Some(Enhancement::Mult),
1384 None,
1385 "3♣ Mult\n",
1386 ),
1387 (
1388 Rank::Four,
1389 Suit::Diamonds,
1390 None,
1391 Some(Edition::Foil),
1392 "4♦ Foil\n",
1393 ),
1394 (
1395 Rank::Five,
1396 Suit::Spades,
1397 Some(Enhancement::Bonus),
1398 Some(Edition::Holographic),
1399 "5♠ Bonus Holographic\n",
1400 ),
1401 (
1402 Rank::Ace,
1403 Suit::Hearts,
1404 Some(Enhancement::Wild),
1405 Some(Edition::Polychrome),
1406 "A♥ Wild Polychrome\n",
1407 ),
1408 ];
1409
1410 for (rank, suit, enhancement, edition, expected) in cases {
1411 let card = Card::new(rank, suit, enhancement, edition);
1412 let serialized = serde_yaml::to_string(&card).unwrap();
1413 assert_eq!(serialized, expected);
1414 }
1415 }
1416
1417 #[test]
1418 fn test_card_deserialization_various_cases() {
1419 let cases = vec![
1420 ("2♥", Card::new(Rank::Two, Suit::Hearts, None, None)),
1421 (
1422 "3♣ Mult",
1423 Card::new(Rank::Three, Suit::Clubs, Some(Enhancement::Mult), None),
1424 ),
1425 (
1426 "4♦ Foil",
1427 Card::new(Rank::Four, Suit::Diamonds, None, Some(Edition::Foil)),
1428 ),
1429 (
1430 "5♠ Bonus Holographic",
1431 Card::new(
1432 Rank::Five,
1433 Suit::Spades,
1434 Some(Enhancement::Bonus),
1435 Some(Edition::Holographic),
1436 ),
1437 ),
1438 (
1439 "A♥ Steel Polychrome",
1440 Card::new(
1441 Rank::Ace,
1442 Suit::Hearts,
1443 Some(Enhancement::Steel),
1444 Some(Edition::Polychrome),
1445 ),
1446 ),
1447 ];
1448
1449 for (serialized, expected_card) in cases {
1450 let card: Card = serde_yaml::from_str(serialized).unwrap();
1451 assert_eq!(
1452 format!("{card:?}").trim(),
1453 format!("{expected_card:?}").trim(),
1454 "Deserialization failed for {}: expected {:?}, got {:?}",
1455 serialized,
1456 expected_card,
1457 card
1458 );
1459 }
1460 }
1461
1462 #[test]
1463 fn test_joker_card_serialization_various_cases() {
1464 let cases = vec![
1465 (Joker::Joker, None, "Joker\n"),
1466 (Joker::JollyJoker, Some(Edition::Foil), "Jolly Joker Foil\n"),
1467 (
1468 Joker::ZanyJoker,
1469 Some(Edition::Holographic),
1470 "Zany Joker Holographic\n",
1471 ),
1472 (
1473 Joker::MadJoker,
1474 Some(Edition::Polychrome),
1475 "Mad Joker Polychrome\n",
1476 ),
1477 (Joker::CrazyJoker, None, "Crazy Joker\n"),
1478 ];
1479
1480 for (joker, edition, expected) in cases {
1481 let joker_card = JokerCard::new(joker, edition);
1482 let serialized = serde_yaml::to_string(&joker_card).unwrap();
1483 assert_eq!(serialized, expected);
1484 }
1485 }
1486
1487 #[test]
1488 fn test_joker_card_deserialization_various_cases() {
1489 let cases = vec![
1490 ("Joker", JokerCard::new(Joker::Joker, None)),
1491 (
1492 "Jolly Joker Foil",
1493 JokerCard::new(Joker::JollyJoker, Some(Edition::Foil)),
1494 ),
1495 (
1496 "Zany Joker Holographic",
1497 JokerCard::new(Joker::ZanyJoker, Some(Edition::Holographic)),
1498 ),
1499 (
1500 "Mad Joker Polychrome",
1501 JokerCard::new(Joker::MadJoker, Some(Edition::Polychrome)),
1502 ),
1503 ("Crazy Joker", JokerCard::new(Joker::CrazyJoker, None)),
1504 ];
1505
1506 for (serialized, expected_joker_card) in cases {
1507 let joker_card: JokerCard = serde_yaml::from_str(serialized).unwrap();
1508 assert_eq!(
1509 format!("{joker_card:?}").trim(),
1510 format!("{expected_joker_card:?}").trim(),
1511 "Deserialization failed for {}: expected {:?}, got {:?}",
1512 serialized,
1513 expected_joker_card,
1514 joker_card
1515 );
1516 }
1517 }
1518
1519 #[test]
1520 fn test_invalid_card_deserialization_cases() {
1521 let invalid_cases = vec![
1522 "Invalid Card",
1523 "2X",
1524 "3♣ Unknown",
1525 "4♦ Mult Extra",
1526 "5♠ Bonus Holographic Extra",
1527 ];
1528
1529 for serialized in invalid_cases {
1530 let result: Result<Card, _> = serde_yaml::from_str(serialized);
1531 assert!(result.is_err());
1532 }
1533 }
1534
1535 #[test]
1536 fn test_invalid_joker_card_deserialization_cases() {
1537 let invalid_cases = vec![
1538 "Invalid JokerCard",
1539 "Jolly Joker Unknown",
1540 "Zany Joker Holographic Extra",
1541 "Mad Joker Polychrome Extra",
1542 ];
1543
1544 for serialized in invalid_cases {
1545 let result: Result<JokerCard, _> = serde_yaml::from_str(serialized);
1546 assert!(result.is_err());
1547 }
1548 }
1549}