apply rustfmt
authorGeoffrey Allott <geoffrey@allott.email>
Sat, 6 Mar 2021 11:02:20 +0000 (11:02 +0000)
committerGeoffrey Allott <geoffrey@allott.email>
Sat, 6 Mar 2021 13:32:12 +0000 (13:32 +0000)
19 files changed:
rustfmt.toml [new file with mode: 0644]
src/auth.rs
src/card.rs
src/client.rs
src/dealer.rs
src/game/action.rs
src/game/chatroom.rs
src/game/mod.rs
src/game/poker/classify.rs
src/game/poker/holdem.rs
src/game/whist.rs
src/main.rs
src/rng.rs
src/seats.rs
src/server.rs
src/username.rs
src/util/dedup.rs
src/util/max.rs
src/util/timestamp.rs

diff --git a/rustfmt.toml b/rustfmt.toml
new file mode 100644 (file)
index 0000000..b0d7ea9
--- /dev/null
@@ -0,0 +1,3 @@
+max_width = 160
+use_field_init_shorthand = true
+use_small_heuristics = "Max"
index fd3049af0dc7d62f742c256f6d2f5bd2007faf53..92db48ec471d899a076109fbea888d4031e5e730 100644 (file)
@@ -9,7 +9,7 @@ impl Auth {
     pub fn verify(&self, _challenge: &str, signature: &str) -> bool {
         match self {
             Auth::NoLogin => false,
-            Auth::Plain{password} => signature == password,
+            Auth::Plain { password } => signature == password,
         }
     }
 }
index aa09f78bebbe394fb808fe8bcf6ae06d7cdd9e55..8ffb0f06ce30f6f46ceca3e4fc76813ae6dd0abf 100644 (file)
@@ -71,61 +71,61 @@ impl Display for Card {
     }
 }
 
-pub const TWO_OF_CLUBS: Card = Card{rank: Two, suit: Clubs};
-pub const THREE_OF_CLUBS: Card = Card{rank: Three, suit: Clubs};
-pub const FOUR_OF_CLUBS: Card = Card{rank: Four, suit: Clubs};
-pub const FIVE_OF_CLUBS: Card = Card{rank: Five, suit: Clubs};
-pub const SIX_OF_CLUBS: Card = Card{rank: Six, suit: Clubs};
-pub const SEVEN_OF_CLUBS: Card = Card{rank: Seven, suit: Clubs};
-pub const EIGHT_OF_CLUBS: Card = Card{rank: Eight, suit: Clubs};
-pub const NINE_OF_CLUBS: Card = Card{rank: Nine, suit: Clubs};
-pub const TEN_OF_CLUBS: Card = Card{rank: Ten, suit: Clubs};
-pub const JACK_OF_CLUBS: Card = Card{rank: Jack, suit: Clubs};
-pub const QUEEN_OF_CLUBS: Card = Card{rank: Queen, suit: Clubs};
-pub const KING_OF_CLUBS: Card = Card{rank: King, suit: Clubs};
-pub const ACE_OF_CLUBS: Card = Card{rank: Ace, suit: Clubs};
+pub const TWO_OF_CLUBS: Card = Card { rank: Two, suit: Clubs };
+pub const THREE_OF_CLUBS: Card = Card { rank: Three, suit: Clubs };
+pub const FOUR_OF_CLUBS: Card = Card { rank: Four, suit: Clubs };
+pub const FIVE_OF_CLUBS: Card = Card { rank: Five, suit: Clubs };
+pub const SIX_OF_CLUBS: Card = Card { rank: Six, suit: Clubs };
+pub const SEVEN_OF_CLUBS: Card = Card { rank: Seven, suit: Clubs };
+pub const EIGHT_OF_CLUBS: Card = Card { rank: Eight, suit: Clubs };
+pub const NINE_OF_CLUBS: Card = Card { rank: Nine, suit: Clubs };
+pub const TEN_OF_CLUBS: Card = Card { rank: Ten, suit: Clubs };
+pub const JACK_OF_CLUBS: Card = Card { rank: Jack, suit: Clubs };
+pub const QUEEN_OF_CLUBS: Card = Card { rank: Queen, suit: Clubs };
+pub const KING_OF_CLUBS: Card = Card { rank: King, suit: Clubs };
+pub const ACE_OF_CLUBS: Card = Card { rank: Ace, suit: Clubs };
 
-pub const TWO_OF_DIAMONDS: Card = Card{rank: Two, suit: Diamonds};
-pub const THREE_OF_DIAMONDS: Card = Card{rank: Three, suit: Diamonds};
-pub const FOUR_OF_DIAMONDS: Card = Card{rank: Four, suit: Diamonds};
-pub const FIVE_OF_DIAMONDS: Card = Card{rank: Five, suit: Diamonds};
-pub const SIX_OF_DIAMONDS: Card = Card{rank: Six, suit: Diamonds};
-pub const SEVEN_OF_DIAMONDS: Card = Card{rank: Seven, suit: Diamonds};
-pub const EIGHT_OF_DIAMONDS: Card = Card{rank: Eight, suit: Diamonds};
-pub const NINE_OF_DIAMONDS: Card = Card{rank: Nine, suit: Diamonds};
-pub const TEN_OF_DIAMONDS: Card = Card{rank: Ten, suit: Diamonds};
-pub const JACK_OF_DIAMONDS: Card = Card{rank: Jack, suit: Diamonds};
-pub const QUEEN_OF_DIAMONDS: Card = Card{rank: Queen, suit: Diamonds};
-pub const KING_OF_DIAMONDS: Card = Card{rank: King, suit: Diamonds};
-pub const ACE_OF_DIAMONDS: Card = Card{rank: Ace, suit: Diamonds};
+pub const TWO_OF_DIAMONDS: Card = Card { rank: Two, suit: Diamonds };
+pub const THREE_OF_DIAMONDS: Card = Card { rank: Three, suit: Diamonds };
+pub const FOUR_OF_DIAMONDS: Card = Card { rank: Four, suit: Diamonds };
+pub const FIVE_OF_DIAMONDS: Card = Card { rank: Five, suit: Diamonds };
+pub const SIX_OF_DIAMONDS: Card = Card { rank: Six, suit: Diamonds };
+pub const SEVEN_OF_DIAMONDS: Card = Card { rank: Seven, suit: Diamonds };
+pub const EIGHT_OF_DIAMONDS: Card = Card { rank: Eight, suit: Diamonds };
+pub const NINE_OF_DIAMONDS: Card = Card { rank: Nine, suit: Diamonds };
+pub const TEN_OF_DIAMONDS: Card = Card { rank: Ten, suit: Diamonds };
+pub const JACK_OF_DIAMONDS: Card = Card { rank: Jack, suit: Diamonds };
+pub const QUEEN_OF_DIAMONDS: Card = Card { rank: Queen, suit: Diamonds };
+pub const KING_OF_DIAMONDS: Card = Card { rank: King, suit: Diamonds };
+pub const ACE_OF_DIAMONDS: Card = Card { rank: Ace, suit: Diamonds };
 
-pub const TWO_OF_HEARTS: Card = Card{rank: Two, suit: Hearts};
-pub const THREE_OF_HEARTS: Card = Card{rank: Three, suit: Hearts};
-pub const FOUR_OF_HEARTS: Card = Card{rank: Four, suit: Hearts};
-pub const FIVE_OF_HEARTS: Card = Card{rank: Five, suit: Hearts};
-pub const SIX_OF_HEARTS: Card = Card{rank: Six, suit: Hearts};
-pub const SEVEN_OF_HEARTS: Card = Card{rank: Seven, suit: Hearts};
-pub const EIGHT_OF_HEARTS: Card = Card{rank: Eight, suit: Hearts};
-pub const NINE_OF_HEARTS: Card = Card{rank: Nine, suit: Hearts};
-pub const TEN_OF_HEARTS: Card = Card{rank: Ten, suit: Hearts};
-pub const JACK_OF_HEARTS: Card = Card{rank: Jack, suit: Hearts};
-pub const QUEEN_OF_HEARTS: Card = Card{rank: Queen, suit: Hearts};
-pub const KING_OF_HEARTS: Card = Card{rank: King, suit: Hearts};
-pub const ACE_OF_HEARTS: Card = Card{rank: Ace, suit: Hearts};
+pub const TWO_OF_HEARTS: Card = Card { rank: Two, suit: Hearts };
+pub const THREE_OF_HEARTS: Card = Card { rank: Three, suit: Hearts };
+pub const FOUR_OF_HEARTS: Card = Card { rank: Four, suit: Hearts };
+pub const FIVE_OF_HEARTS: Card = Card { rank: Five, suit: Hearts };
+pub const SIX_OF_HEARTS: Card = Card { rank: Six, suit: Hearts };
+pub const SEVEN_OF_HEARTS: Card = Card { rank: Seven, suit: Hearts };
+pub const EIGHT_OF_HEARTS: Card = Card { rank: Eight, suit: Hearts };
+pub const NINE_OF_HEARTS: Card = Card { rank: Nine, suit: Hearts };
+pub const TEN_OF_HEARTS: Card = Card { rank: Ten, suit: Hearts };
+pub const JACK_OF_HEARTS: Card = Card { rank: Jack, suit: Hearts };
+pub const QUEEN_OF_HEARTS: Card = Card { rank: Queen, suit: Hearts };
+pub const KING_OF_HEARTS: Card = Card { rank: King, suit: Hearts };
+pub const ACE_OF_HEARTS: Card = Card { rank: Ace, suit: Hearts };
 
-pub const TWO_OF_SPADES: Card = Card{rank: Two, suit: Spades};
-pub const THREE_OF_SPADES: Card = Card{rank: Three, suit: Spades};
-pub const FOUR_OF_SPADES: Card = Card{rank: Four, suit: Spades};
-pub const FIVE_OF_SPADES: Card = Card{rank: Five, suit: Spades};
-pub const SIX_OF_SPADES: Card = Card{rank: Six, suit: Spades};
-pub const SEVEN_OF_SPADES: Card = Card{rank: Seven, suit: Spades};
-pub const EIGHT_OF_SPADES: Card = Card{rank: Eight, suit: Spades};
-pub const NINE_OF_SPADES: Card = Card{rank: Nine, suit: Spades};
-pub const TEN_OF_SPADES: Card = Card{rank: Ten, suit: Spades};
-pub const JACK_OF_SPADES: Card = Card{rank: Jack, suit: Spades};
-pub const QUEEN_OF_SPADES: Card = Card{rank: Queen, suit: Spades};
-pub const KING_OF_SPADES: Card = Card{rank: King, suit: Spades};
-pub const ACE_OF_SPADES: Card = Card{rank: Ace, suit: Spades};
+pub const TWO_OF_SPADES: Card = Card { rank: Two, suit: Spades };
+pub const THREE_OF_SPADES: Card = Card { rank: Three, suit: Spades };
+pub const FOUR_OF_SPADES: Card = Card { rank: Four, suit: Spades };
+pub const FIVE_OF_SPADES: Card = Card { rank: Five, suit: Spades };
+pub const SIX_OF_SPADES: Card = Card { rank: Six, suit: Spades };
+pub const SEVEN_OF_SPADES: Card = Card { rank: Seven, suit: Spades };
+pub const EIGHT_OF_SPADES: Card = Card { rank: Eight, suit: Spades };
+pub const NINE_OF_SPADES: Card = Card { rank: Nine, suit: Spades };
+pub const TEN_OF_SPADES: Card = Card { rank: Ten, suit: Spades };
+pub const JACK_OF_SPADES: Card = Card { rank: Jack, suit: Spades };
+pub const QUEEN_OF_SPADES: Card = Card { rank: Queen, suit: Spades };
+pub const KING_OF_SPADES: Card = Card { rank: King, suit: Spades };
+pub const ACE_OF_SPADES: Card = Card { rank: Ace, suit: Spades };
 
 pub const FIFTY_TWO_CARD_DECK: [Card; 52] = [
     ACE_OF_SPADES,
index 2ff9f91c4d57a2bf00ba65356b24ce4836977e24..b2bee747ccf3db510f7328b05f83a188a894fd0f 100644 (file)
@@ -1,6 +1,6 @@
 use std::collections::HashSet;
 
-use futures::stream::{Stream, StreamExt, empty, iter, once};
+use futures::stream::{empty, iter, once, Stream, StreamExt};
 
 use crate::api::{ClientMessage, ServerMessage};
 use crate::game::{Game, GameList, UserAction};
@@ -16,10 +16,7 @@ pub struct ConnectionState {
 pub enum ClientState {
     Connected,
     LoginAuthIssued { username: Username, challenge: String },
-    LoggedIn {
-        username: Username,
-        state: LoggedInState,
-    },
+    LoggedIn { username: Username, state: LoggedInState },
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -39,31 +36,28 @@ pub enum LoggedInState {
 
 impl ConnectionState {
     pub fn new(server: ServerState) -> Self {
-        Self {
-            server,
-            client: ClientState::Connected,
-        }
+        Self { server, client: ClientState::Connected }
     }
 
-    pub async fn retrieve_updates(&mut self, update: ClientInterest) -> impl Stream<Item=ServerMessage> {
+    pub async fn retrieve_updates(&mut self, update: ClientInterest) -> impl Stream<Item = ServerMessage> {
         match update {
             ClientInterest::GameList => match &mut self.client {
-                ClientState::LoggedIn{state: LoggedInState::InLobby{ref mut game_list}, ..} => {
+                ClientState::LoggedIn { state: LoggedInState::InLobby { ref mut game_list }, .. } => {
                     let from = game_list.games_len();
                     match self.server.game_list(from).await {
                         Ok(games) => {
                             for game in games.clone() {
                                 game_list.push(game);
                             }
-                            iter(games).map(|game| ServerMessage::NewGame{game}).boxed()
+                            iter(games).map(|game| ServerMessage::NewGame { game }).boxed()
                         }
-                        Err(err) => once(async move { ServerMessage::ProtocolError{reason: err.to_string()} }).boxed(),
+                        Err(err) => once(async move { ServerMessage::ProtocolError { reason: err.to_string() } }).boxed(),
                     }
                 }
                 _ => empty().boxed(),
-            }
-            ClientInterest::Game{id} => match &mut self.client {
-                &mut ClientState::LoggedIn{username, state: LoggedInState::InGame{ref mut game}} if game.id() == id => {
+            },
+            ClientInterest::Game { id } => match &mut self.client {
+                &mut ClientState::LoggedIn { username, state: LoggedInState::InGame { ref mut game } } if game.id() == id => {
                     let id = game.id();
                     let from = game.actions_len();
                     match self.server.game_state(id, from).await {
@@ -74,31 +68,31 @@ impl ConnectionState {
                                     error!("Action from database failed to apply: {}", err);
                                 }
                             }
-                            iter(actions_view).map(|action| ServerMessage::NewAction{action}).boxed()
+                            iter(actions_view).map(|action| ServerMessage::NewAction { action }).boxed()
                         }
-                        Err(err) => once(async move { ServerMessage::ProtocolError{reason: err.to_string()} }).boxed(),
+                        Err(err) => once(async move { ServerMessage::ProtocolError { reason: err.to_string() } }).boxed(),
                     }
                 }
                 _ => empty().boxed(),
-            }
-            ClientInterest::User{..} => empty().boxed(), // TODO
-            ClientInterest::Timeout{..} => empty().boxed(), // TODO
+            },
+            ClientInterest::User { .. } => empty().boxed(),    // TODO
+            ClientInterest::Timeout { .. } => empty().boxed(), // TODO
         }
     }
 
     pub fn interests(&self) -> HashSet<ClientInterest> {
         let mut ret = HashSet::new();
-        if let &ClientState::LoggedIn{username, ref state} = &self.client {
-            ret.insert(ClientInterest::User{username});
+        if let &ClientState::LoggedIn { username, ref state } = &self.client {
+            ret.insert(ClientInterest::User { username });
             match state {
-                LoggedInState::Idle => {},
-                LoggedInState::InLobby{..} => {
+                LoggedInState::Idle => {}
+                LoggedInState::InLobby { .. } => {
                     ret.insert(ClientInterest::GameList);
                 }
-                LoggedInState::InGame{ref game} => {
-                    ret.insert(ClientInterest::Game{id: game.id()});
+                LoggedInState::InGame { ref game } => {
+                    ret.insert(ClientInterest::Game { id: game.id() });
                     for username in game.players() {
-                        ret.insert(ClientInterest::User{username});
+                        ret.insert(ClientInterest::User { username });
                     }
                 }
             }
@@ -119,46 +113,42 @@ impl ConnectionState {
 
     async fn message_response(&mut self, message: ClientMessage) -> ServerMessage {
         match (&mut self.client, message) {
-            (_, ClientMessage::CreateUser{username, auth, nickname}) => {
-                match self.server.create_user(username, auth, &nickname).await {
-                    Ok(()) => ServerMessage::CreateUserSuccess,
-                    Err(_) => ServerMessage::CreateUserFailure{reason: "User already exists".to_string()},
-                }
-            }
-            (&mut ClientState::Connected, ClientMessage::Login{username}) => {
+            (_, ClientMessage::CreateUser { username, auth, nickname }) => match self.server.create_user(username, auth, &nickname).await {
+                Ok(()) => ServerMessage::CreateUserSuccess,
+                Err(_) => ServerMessage::CreateUserFailure { reason: "User already exists".to_string() },
+            },
+            (&mut ClientState::Connected, ClientMessage::Login { username }) => {
                 let challenge = format!("{:032x}{:032x}", rand::random::<u128>(), rand::random::<u128>());
-                self.client = ClientState::LoginAuthIssued{username, challenge: challenge.clone()};
-                ServerMessage::LoginAuthChallenge{challenge}
+                self.client = ClientState::LoginAuthIssued { username, challenge: challenge.clone() };
+                ServerMessage::LoginAuthChallenge { challenge }
             }
-            (&mut ClientState::LoginAuthIssued{username, ref challenge}, ClientMessage::LoginAuthResponse{signature}) => {
+            (&mut ClientState::LoginAuthIssued { username, ref challenge }, ClientMessage::LoginAuthResponse { signature }) => {
                 if self.server.verify(username, &challenge, &signature).await {
-                    self.client = ClientState::LoggedIn{username: username.clone(), state: LoggedInState::Idle};
+                    self.client = ClientState::LoggedIn { username: username.clone(), state: LoggedInState::Idle };
                     ServerMessage::LoginSuccess
                 } else {
                     self.client = ClientState::Connected;
-                    ServerMessage::LoginFailure{reason: "Invalid username or password".to_string()}
+                    ServerMessage::LoginFailure { reason: "Invalid username or password".to_string() }
                 }
             }
-            (&mut ClientState::LoggedIn{username, ..}, ClientMessage::JoinLobby{filter}) => {
+            (&mut ClientState::LoggedIn { username, .. }, ClientMessage::JoinLobby { filter }) => {
                 let mut game_list = GameList::new(filter);
                 match self.server.game_list(0).await {
                     Ok(games) => {
                         for game in games.clone() {
                             game_list.push(game);
                         }
-                        self.client = ClientState::LoggedIn{username: username.clone(), state: LoggedInState::InLobby{game_list}};
-                        ServerMessage::JoinLobbySuccess{games}
+                        self.client = ClientState::LoggedIn { username: username.clone(), state: LoggedInState::InLobby { game_list } };
+                        ServerMessage::JoinLobbySuccess { games }
                     }
-                    Err(err) => ServerMessage::JoinLobbyFailure{reason: err.to_string()},
-                }
-            }
-            (&mut ClientState::LoggedIn{..}, ClientMessage::CreateGame{settings}) => {
-                match self.server.create_game(settings).await {
-                    Ok(id) => ServerMessage::CreateGameSuccess{id},
-                    Err(err) => ServerMessage::CreateGameFailure{reason: err.to_string()},
+                    Err(err) => ServerMessage::JoinLobbyFailure { reason: err.to_string() },
                 }
             }
-            (&mut ClientState::LoggedIn{username, ..}, ClientMessage::JoinGame{id}) => {
+            (&mut ClientState::LoggedIn { .. }, ClientMessage::CreateGame { settings }) => match self.server.create_game(settings).await {
+                Ok(id) => ServerMessage::CreateGameSuccess { id },
+                Err(err) => ServerMessage::CreateGameFailure { reason: err.to_string() },
+            },
+            (&mut ClientState::LoggedIn { username, .. }, ClientMessage::JoinGame { id }) => {
                 match (self.server.game_summary(id).await, self.server.game_state(id, 0).await, self.server.game_seed(id).await) {
                     (Ok(summary), Ok(actions), Ok(seed)) => {
                         let actions_view = actions.iter().map(|action| action.view_for(username)).collect();
@@ -168,79 +158,73 @@ impl ConnectionState {
                                 error!("Action from database failed to apply: {}", err);
                             }
                         }
-                        self.client = ClientState::LoggedIn{username: username.clone(), state: LoggedInState::InGame{game}};
-                        ServerMessage::JoinGameSuccess{summary, actions: actions_view}
+                        self.client = ClientState::LoggedIn { username: username.clone(), state: LoggedInState::InGame { game } };
+                        ServerMessage::JoinGameSuccess { summary, actions: actions_view }
                     }
-                    (Err(err), _, _) | (_, Err(err), _) | (_, _, Err(err)) => ServerMessage::JoinGameFailure{reason: err.to_string()},
+                    (Err(err), _, _) | (_, Err(err), _) | (_, _, Err(err)) => ServerMessage::JoinGameFailure { reason: err.to_string() },
                 }
             }
-            (&mut ClientState::LoggedIn{username, state: LoggedInState::InGame{ref mut game}}, ClientMessage::TakeAction{action}) => {
+            (&mut ClientState::LoggedIn { username, state: LoggedInState::InGame { ref mut game } }, ClientMessage::TakeAction { action }) => {
                 let timestamp = match self.server.now().await {
                     Ok(timestamp) => timestamp,
-                    Err(err) => return ServerMessage::InternalError{reason: err.to_string()},
+                    Err(err) => return ServerMessage::InternalError { reason: err.to_string() },
                 };
-                let action = UserAction{timestamp, username, action};
+                let action = UserAction { timestamp, username, action };
                 let id = game.id();
                 loop {
                     let len = game.actions_len();
                     match game.validate_action(action.clone()) {
                         Ok(validated) => match self.server.take_action(id, len, &validated).await {
                             Ok(ActionStatus::Committed) => match game.take_action(validated.clone()) {
-                                Ok(()) => return ServerMessage::TakeActionSuccess{action},
-                                Err(err) => return ServerMessage::TakeActionFailure{action, reason: err.to_string()},
-                            }
+                                Ok(()) => return ServerMessage::TakeActionSuccess { action },
+                                Err(err) => return ServerMessage::TakeActionFailure { action, reason: err.to_string() },
+                            },
                             Ok(ActionStatus::Interrupted) => {
                                 debug!("Action {:?} was interrupted - updating game state", action);
                                 match self.server.game_state(id, len).await {
-                                    Ok(actions) => for new_action in actions {
-                                        if let Err(err) = game.take_action(new_action) {
-                                            return ServerMessage::TakeActionFailure{action, reason: err.to_string()};
+                                    Ok(actions) => {
+                                        for new_action in actions {
+                                            if let Err(err) = game.take_action(new_action) {
+                                                return ServerMessage::TakeActionFailure { action, reason: err.to_string() };
+                                            }
                                         }
                                     }
                                     Err(err) => {
-                                        return ServerMessage::TakeActionFailure{action, reason: err.to_string()};
+                                        return ServerMessage::TakeActionFailure { action, reason: err.to_string() };
                                     }
                                 }
                             }
-                            Err(err) => return ServerMessage::TakeActionFailure{action, reason: err.to_string()},
-                        }
-                        Err(err) => return ServerMessage::TakeActionFailure{action, reason: err.to_string()},
+                            Err(err) => return ServerMessage::TakeActionFailure { action, reason: err.to_string() },
+                        },
+                        Err(err) => return ServerMessage::TakeActionFailure { action, reason: err.to_string() },
                     }
                 }
             }
-            (&mut ClientState::LoggedIn{username, state: LoggedInState::InLobby{..}}, ClientMessage::LeaveLobby) => {
-                self.client = ClientState::LoggedIn{username, state: LoggedInState::Idle};
+            (&mut ClientState::LoggedIn { username, state: LoggedInState::InLobby { .. } }, ClientMessage::LeaveLobby) => {
+                self.client = ClientState::LoggedIn { username, state: LoggedInState::Idle };
                 ServerMessage::LeaveLobbySuccess
             }
-            (&mut ClientState::LoggedIn{..}, ClientMessage::LeaveLobby) => {
-                ServerMessage::LeaveLobbyFailure{reason: "Not in lobby".to_string()}
-            }
-            (&mut ClientState::LoggedIn{username, state: LoggedInState::InGame{..}}, ClientMessage::LeaveGame) => {
-                self.client = ClientState::LoggedIn{username, state: LoggedInState::Idle};
+            (&mut ClientState::LoggedIn { .. }, ClientMessage::LeaveLobby) => ServerMessage::LeaveLobbyFailure { reason: "Not in lobby".to_string() },
+            (&mut ClientState::LoggedIn { username, state: LoggedInState::InGame { .. } }, ClientMessage::LeaveGame) => {
+                self.client = ClientState::LoggedIn { username, state: LoggedInState::Idle };
                 ServerMessage::LeaveGameSuccess
             }
-            (&mut ClientState::LoggedIn{..}, ClientMessage::LeaveGame) => {
-                ServerMessage::LeaveGameFailure{reason: "Not in game".to_string()}
-            }
-            (&mut ClientState::LoggedIn{..}, ClientMessage::Logout) => {
+            (&mut ClientState::LoggedIn { .. }, ClientMessage::LeaveGame) => ServerMessage::LeaveGameFailure { reason: "Not in game".to_string() },
+            (&mut ClientState::LoggedIn { .. }, ClientMessage::Logout) => {
                 self.client = ClientState::Connected;
                 ServerMessage::LogoutSuccess
             }
-            (&mut ClientState::LoggedIn{username, ..}, ClientMessage::ChangeAuth{auth}) => {
-                match self.server.set_user_auth(username, auth).await {
-                    Ok(()) => ServerMessage::ChangeAuthSuccess,
-                    Err(err) => ServerMessage::ChangeAuthFailure{reason: err.to_string()},
-                }
-            }
-            (&mut ClientState::LoggedIn{username, ..}, ClientMessage::ChangeNickname{nickname}) => {
+            (&mut ClientState::LoggedIn { username, .. }, ClientMessage::ChangeAuth { auth }) => match self.server.set_user_auth(username, auth).await {
+                Ok(()) => ServerMessage::ChangeAuthSuccess,
+                Err(err) => ServerMessage::ChangeAuthFailure { reason: err.to_string() },
+            },
+            (&mut ClientState::LoggedIn { username, .. }, ClientMessage::ChangeNickname { nickname }) => {
                 match self.server.set_user_nickname(username, &nickname).await {
                     Ok(()) => ServerMessage::ChangeNicknameSuccess,
-                    Err(err) => ServerMessage::ChangeNicknameFailure{reason: err.to_string()},
+                    Err(err) => ServerMessage::ChangeNicknameFailure { reason: err.to_string() },
                 }
             }
-            (_, _) => {
-                ServerMessage::ProtocolError{reason: "Protocol error".to_string() }
-            }
+            (_, _) => ServerMessage::ProtocolError { reason: "Protocol error".to_string() },
         }
     }
 }
index 12e1babb12b3d826d5783523295383f07d225db7..c7a8b62c756c562e975f75de4f69079cc1b27500 100644 (file)
@@ -4,7 +4,7 @@ use futures::{channel::mpsc::Receiver, StreamExt};
 use redis::{ErrorKind, RedisError, RedisResult};
 
 use crate::client::ClientInterest;
-use crate::game::{Game, DealerAction, ValidatedUserAction};
+use crate::game::{DealerAction, Game, ValidatedUserAction};
 use crate::server::{ActionStatus, ServerState};
 use crate::util::dedup::DedupReadyExt;
 
@@ -26,13 +26,13 @@ enum Termination {
 impl Dealer {
     pub async fn new(mut server: ServerState, id: i64) -> RedisResult<Self> {
         let mut interests = HashSet::new();
-        interests.insert(ClientInterest::Game{id});
-        interests.insert(ClientInterest::Timeout{id});
+        interests.insert(ClientInterest::Game { id });
+        interests.insert(ClientInterest::Timeout { id });
         server.register_interests(interests).await;
         let summary = server.game_summary(id).await?;
         let seed = server.game_seed(id).await?;
         let game = Game::new(summary, seed);
-        let mut dealer = Dealer{server, dealer: DealerState{game}};
+        let mut dealer = Dealer { server, dealer: DealerState { game } };
         dealer.retrieve_updates().await?;
         Ok(dealer)
     }
@@ -94,7 +94,7 @@ impl Dealer {
             ActionStatus::Committed => match game.take_action(action) {
                 Ok(()) => return Ok(ActionStatus::Committed),
                 Err(err) => return Err(RedisError::from((ErrorKind::ClientError, "Invalid action", err.to_string()))),
-            }
+            },
             ActionStatus::Interrupted => {
                 debug!("Action {:?} was interrupted", action);
                 Ok(ActionStatus::Interrupted)
index 15c99c293153ee5c98ed2ebb1bb8f93121057fe0..85fac7bc58fa04a39ace17d77fc171138cf72f59 100644 (file)
@@ -62,7 +62,7 @@ pub enum Action {
 impl Action {
     pub fn anonymise(&self) -> Self {
         match self {
-            Action::ReceiveCard{..} => Action::ReceiveCard{card: None},
+            Action::ReceiveCard { .. } => Action::ReceiveCard { card: None },
             action => action.clone(),
         }
     }
index 6cd839938937a49caebf711ecd4ddedda58ebe6e..02d7467fdfdea2b0b5682eedc83afed282268944 100644 (file)
@@ -1,11 +1,11 @@
 use std::collections::HashSet;
 
-use crate::username::{DEALER, Username};
 use crate::game::{Action, ActionError, DealerAction};
+use crate::username::{Username, DEALER};
 use crate::util::timestamp::Timestamp;
 
-use super::{Game, UserAction};
 use super::ValidatedUserAction;
+use super::{Game, UserAction};
 
 #[derive(Debug, Clone)]
 enum ChatroomAction {
@@ -36,12 +36,7 @@ pub struct Chatroom {
 
 impl Chatroom {
     pub fn new(id: i64, settings: ChatroomSettings) -> Self {
-        Chatroom {
-            id,
-            settings,
-            messages: Vec::new(),
-            users: HashSet::new(),
-        }
+        Chatroom { id, settings, messages: Vec::new(), users: HashSet::new() }
     }
 }
 
@@ -60,29 +55,29 @@ impl Game for Chatroom {
 
     fn validate_action(&self, action: UserAction) -> Result<ValidatedUserAction, ActionError> {
         match action.action {
-            Action::Join{..} if !self.users.contains(&action.username) => Ok(ValidatedUserAction(action)),
-            Action::Join{..} => Err(ActionError::AlreadyJoined),
-            Action::Message{..} if self.users.contains(&action.username) => Ok(ValidatedUserAction(action)),
-            Action::Message{..} => Err(ActionError::NotAuthorised),
+            Action::Join { .. } if !self.users.contains(&action.username) => Ok(ValidatedUserAction(action)),
+            Action::Join { .. } => Err(ActionError::AlreadyJoined),
+            Action::Message { .. } if self.users.contains(&action.username) => Ok(ValidatedUserAction(action)),
+            Action::Message { .. } => Err(ActionError::NotAuthorised),
             Action::Leave if self.users.contains(&action.username) => Ok(ValidatedUserAction(action)),
             Action::Leave => Err(ActionError::NotAuthorised),
             _ => Err(ActionError::InvalidActionForGameType),
         }
     }
 
-    fn take_action(&mut self, ValidatedUserAction(UserAction{timestamp, username, action}): ValidatedUserAction) -> Result<(), ActionError> {
+    fn take_action(&mut self, ValidatedUserAction(UserAction { timestamp, username, action }): ValidatedUserAction) -> Result<(), ActionError> {
         match action {
-            Action::Join{..} => {
-                self.messages.push(ChatroomUserAction{timestamp, username, action: ChatroomAction::Join});
+            Action::Join { .. } => {
+                self.messages.push(ChatroomUserAction { timestamp, username, action: ChatroomAction::Join });
                 self.users.insert(username);
                 Ok(())
             }
-            Action::Message{message} => {
-                self.messages.push(ChatroomUserAction{timestamp, username, action: ChatroomAction::Message(message)});
+            Action::Message { message } => {
+                self.messages.push(ChatroomUserAction { timestamp, username, action: ChatroomAction::Message(message) });
                 Ok(())
             }
             Action::Leave => {
-                self.messages.push(ChatroomUserAction{timestamp, username, action: ChatroomAction::Leave});
+                self.messages.push(ChatroomUserAction { timestamp, username, action: ChatroomAction::Leave });
                 self.users.remove(&username);
                 Ok(())
             }
@@ -92,13 +87,11 @@ impl Game for Chatroom {
 
     fn next_dealer_action(&self, timestamp: Timestamp) -> DealerAction {
         match self.messages.len() {
-            n if n % 10 == 0 => DealerAction::TakeAction(
-                ValidatedUserAction(UserAction{
-                    timestamp,
-                    username: DEALER,
-                    action: Action::Message{message: format!("{} messages posted so far", n)}
-                })
-            ),
+            n if n % 10 == 0 => DealerAction::TakeAction(ValidatedUserAction(UserAction {
+                timestamp,
+                username: DEALER,
+                action: Action::Message { message: format!("{} messages posted so far", n) },
+            })),
             _ => DealerAction::WaitForPlayer,
         }
     }
index e7088b76820deed3f219ac77c2750ba35984fe93..a11567734a51ba6f3345a19e41e00878e293c7fa 100644 (file)
@@ -11,12 +11,12 @@ use crate::username::Username;
 use crate::util::timestamp::Timestamp;
 
 use self::chatroom::{Chatroom, ChatroomSettings};
-use self::whist::{KnockOutWhist, KnockOutWhistSettings};
 use self::poker::{TexasHoldEm, TexasHoldEmSettings};
+use self::whist::{KnockOutWhist, KnockOutWhistSettings};
 
 pub use self::action::{Action, ActionError, DealerAction, UserAction, ValidatedUserAction};
 
-pub trait Game : Debug + CloneBoxGame + Send + Sync {
+pub trait Game: Debug + CloneBoxGame + Send + Sync {
     fn id(&self) -> i64;
     fn players(&self) -> HashSet<Username>;
     fn actions_len(&self) -> usize;
@@ -42,7 +42,7 @@ impl Clone for Box<dyn Game> {
 }
 
 impl dyn Game {
-    pub fn new(GameSummary{id, settings}: GameSummary, seed: Seed) -> Box<Self> {
+    pub fn new(GameSummary { id, settings }: GameSummary, seed: Seed) -> Box<Self> {
         match settings {
             GameSettings::Chatroom(settings) => Box::new(Chatroom::new(id, settings)),
             GameSettings::KnockOutWhist(settings) => Box::new(KnockOutWhist::new(id, settings, seed)),
@@ -68,10 +68,7 @@ pub struct GameList {
 
 impl GameList {
     pub fn new(filter: String) -> Self {
-        Self {
-            filter,
-            games: Vec::new(),
-        }
+        Self { filter, games: Vec::new() }
     }
 
     pub fn games_len(&self) -> usize {
@@ -91,7 +88,7 @@ pub struct GameSummary {
 
 impl GameSummary {
     pub fn new(id: i64, settings: GameSettings) -> Self {
-        Self{id, settings}
+        Self { id, settings }
     }
 
     pub fn id(&self) -> i64 {
index 99124bc47a211bf9555a4a0796d0ba4c4cc831bd..a75d0294e5a4025b7dc4911a057e41f28cf4ca41 100644 (file)
@@ -1,38 +1,40 @@
 use std::cmp::Ordering;
 use std::fmt::{self, Display, Formatter};
 
-use crate::card::*;
 use crate::card::Rank::*;
 use crate::card::Suit::*;
+use crate::card::*;
 
 use self::Hand::*;
 
 #[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug)]
 pub enum Hand {
-    HighCard{kickers: (Rank, Rank, Rank, Rank, Rank)},
-    Pair{pair: Rank, kickers: (Rank, Rank, Rank)},
-    TwoPair{pairs: (Rank, Rank), kicker: Rank},
-    ThreeOfAKind{trips: Rank, kickers: (Rank, Rank)},
-    Straight{high_card: Rank},
-    Flush{flush: (Rank, Rank, Rank, Rank, Rank)},
-    FullHouse{trips: Rank, pair: Rank},
-    FourOfAKind{quads: Rank, kicker: Rank},
-    StraightFlush{high_card: Rank},
+    HighCard { kickers: (Rank, Rank, Rank, Rank, Rank) },
+    Pair { pair: Rank, kickers: (Rank, Rank, Rank) },
+    TwoPair { pairs: (Rank, Rank), kicker: Rank },
+    ThreeOfAKind { trips: Rank, kickers: (Rank, Rank) },
+    Straight { high_card: Rank },
+    Flush { flush: (Rank, Rank, Rank, Rank, Rank) },
+    FullHouse { trips: Rank, pair: Rank },
+    FourOfAKind { quads: Rank, kicker: Rank },
+    StraightFlush { high_card: Rank },
     RoyalFlush,
 }
 
 impl Display for Hand {
     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
         match *self {
-            HighCard{kickers: (kicker1, kicker2, kicker3, kicker4, kicker5)} => write!(f, "High Card {}, {}{}{}{} Kickers", kicker1, kicker2, kicker3, kicker4, kicker5),
-            Pair{pair, kickers: (kicker1, kicker2, kicker3)} => write!(f, "Pair of {}s, {}{}{} Kickers", pair, kicker1, kicker2, kicker3),
-            TwoPair{pairs: (pair1, pair2), kicker} => write!(f, "Two Pair, {}s & {}s, {} Kicker", pair1, pair2, kicker),
-            ThreeOfAKind{trips, kickers: (kicker1, kicker2)} => write!(f, "Three of a Kind, {}s, {}{} Kickers", trips, kicker1, kicker2),
-            Straight{high_card} => write!(f, "Straight, {} High", high_card),
-            Flush{flush: (rank1, rank2, rank3, rank4, rank5)} => write!(f, "Flush, {}{}{}{}{}", rank1, rank2, rank3, rank4, rank5),
-            FullHouse{trips, pair} => write!(f, "Full House, {}s full of {}s", trips, pair),
-            FourOfAKind{quads, kicker} => write!(f, "Four of a Kind, {}s, {} Kicker", quads, kicker),
-            StraightFlush{high_card} => write!(f, "Straight Flush, {} High", high_card),
+            HighCard { kickers: (kicker1, kicker2, kicker3, kicker4, kicker5) } => {
+                write!(f, "High Card {}, {}{}{}{} Kickers", kicker1, kicker2, kicker3, kicker4, kicker5)
+            }
+            Pair { pair, kickers: (kicker1, kicker2, kicker3) } => write!(f, "Pair of {}s, {}{}{} Kickers", pair, kicker1, kicker2, kicker3),
+            TwoPair { pairs: (pair1, pair2), kicker } => write!(f, "Two Pair, {}s & {}s, {} Kicker", pair1, pair2, kicker),
+            ThreeOfAKind { trips, kickers: (kicker1, kicker2) } => write!(f, "Three of a Kind, {}s, {}{} Kickers", trips, kicker1, kicker2),
+            Straight { high_card } => write!(f, "Straight, {} High", high_card),
+            Flush { flush: (rank1, rank2, rank3, rank4, rank5) } => write!(f, "Flush, {}{}{}{}{}", rank1, rank2, rank3, rank4, rank5),
+            FullHouse { trips, pair } => write!(f, "Full House, {}s full of {}s", trips, pair),
+            FourOfAKind { quads, kicker } => write!(f, "Four of a Kind, {}s, {} Kicker", quads, kicker),
+            StraightFlush { high_card } => write!(f, "Straight Flush, {} High", high_card),
             RoyalFlush => write!(f, "Royal Flush"),
         }
     }
@@ -45,395 +47,391 @@ pub fn rank_7_card_hand(mut cards: [Card; 7]) -> Hand {
     let suits = [cards[0].suit, cards[1].suit, cards[2].suit, cards[3].suit, cards[4].suit, cards[5].suit, cards[6].suit];
 
     match suits {
-        [Spades, Spades, Spades, Spades, Spades, Spades, Spades] |
-        [Hearts, Hearts, Hearts, Hearts, Hearts, Hearts, Hearts] |
-        [Diamonds, Diamonds, Diamonds, Diamonds, Diamonds, Diamonds, Diamonds] |
-        [Clubs, Clubs, Clubs, Clubs, Clubs, Clubs, Clubs] =>
-            match ranks {
-                [Ace, King, Queen, Jack, Ten, _, _] => RoyalFlush,
-                [King, Queen, Jack, Ten, Nine, _, _] => StraightFlush{high_card: King},
-                [Queen, Jack, Ten, Nine, Eight, _, _] => StraightFlush{high_card: Queen},
-                [_, Queen, Jack, Ten, Nine, Eight, _] => StraightFlush{high_card: Queen},
-                [Jack, Ten, Nine, Eight, Seven, _, _] => StraightFlush{high_card: Jack},
-                [_, Jack, Ten, Nine, Eight, Seven, _] => StraightFlush{high_card: Jack},
-                [_, _, Jack, Ten, Nine, Eight, Seven] => StraightFlush{high_card: Jack},
-                [Ten, Nine, Eight, Seven, Six, _, _] => StraightFlush{high_card: Ten},
-                [_, Ten, Nine, Eight, Seven, Six, _] => StraightFlush{high_card: Ten},
-                [_, _, Ten, Nine, Eight, Seven, Six] => StraightFlush{high_card: Ten},
-                [Nine, Eight, Seven, Six, Five, _, _] => StraightFlush{high_card: Nine},
-                [_, Nine, Eight, Seven, Six, Five, _] => StraightFlush{high_card: Nine},
-                [_, _, Nine, Eight, Seven, Six, Five] => StraightFlush{high_card: Nine},
-                [Eight, Seven, Six, Five, Four, _, _] => StraightFlush{high_card: Eight},
-                [_, Eight, Seven, Six, Five, Four, _] => StraightFlush{high_card: Eight},
-                [_, _, Eight, Seven, Six, Five, Four] => StraightFlush{high_card: Eight},
-                [Seven, Six, Five, Four, Three, _, _] => StraightFlush{high_card: Seven},
-                [_, Seven, Six, Five, Four, Three, _] => StraightFlush{high_card: Seven},
-                [_, _, Seven, Six, Five, Four, Three] => StraightFlush{high_card: Seven},
-                [Six, Five, Four, Three, Two, _, _] => StraightFlush{high_card: Six},
-                [_, Six, Five, Four, Three, Two, _] => StraightFlush{high_card: Six},
-                [_, _, Six, Five, Four, Three, Two] => StraightFlush{high_card: Six},
-                [Ace, _, _, Five, Four, Three, Two] => StraightFlush{high_card: Five},
-                [rank1, rank2, rank3, rank4, rank5, _, _] => Flush{flush: (rank1, rank2, rank3, rank4, rank5)},
-            }
-        [Spades, Spades, Spades, Spades, Spades, Spades, _] |
-        [Hearts, Hearts, Hearts, Hearts, Hearts, Hearts, _] |
-        [Diamonds, Diamonds, Diamonds, Diamonds, Diamonds, Diamonds, _] =>
+        [Spades, Spades, Spades, Spades, Spades, Spades, Spades]
+        | [Hearts, Hearts, Hearts, Hearts, Hearts, Hearts, Hearts]
+        | [Diamonds, Diamonds, Diamonds, Diamonds, Diamonds, Diamonds, Diamonds]
+        | [Clubs, Clubs, Clubs, Clubs, Clubs, Clubs, Clubs] => match ranks {
+            [Ace, King, Queen, Jack, Ten, _, _] => RoyalFlush,
+            [King, Queen, Jack, Ten, Nine, _, _] => StraightFlush { high_card: King },
+            [Queen, Jack, Ten, Nine, Eight, _, _] => StraightFlush { high_card: Queen },
+            [_, Queen, Jack, Ten, Nine, Eight, _] => StraightFlush { high_card: Queen },
+            [Jack, Ten, Nine, Eight, Seven, _, _] => StraightFlush { high_card: Jack },
+            [_, Jack, Ten, Nine, Eight, Seven, _] => StraightFlush { high_card: Jack },
+            [_, _, Jack, Ten, Nine, Eight, Seven] => StraightFlush { high_card: Jack },
+            [Ten, Nine, Eight, Seven, Six, _, _] => StraightFlush { high_card: Ten },
+            [_, Ten, Nine, Eight, Seven, Six, _] => StraightFlush { high_card: Ten },
+            [_, _, Ten, Nine, Eight, Seven, Six] => StraightFlush { high_card: Ten },
+            [Nine, Eight, Seven, Six, Five, _, _] => StraightFlush { high_card: Nine },
+            [_, Nine, Eight, Seven, Six, Five, _] => StraightFlush { high_card: Nine },
+            [_, _, Nine, Eight, Seven, Six, Five] => StraightFlush { high_card: Nine },
+            [Eight, Seven, Six, Five, Four, _, _] => StraightFlush { high_card: Eight },
+            [_, Eight, Seven, Six, Five, Four, _] => StraightFlush { high_card: Eight },
+            [_, _, Eight, Seven, Six, Five, Four] => StraightFlush { high_card: Eight },
+            [Seven, Six, Five, Four, Three, _, _] => StraightFlush { high_card: Seven },
+            [_, Seven, Six, Five, Four, Three, _] => StraightFlush { high_card: Seven },
+            [_, _, Seven, Six, Five, Four, Three] => StraightFlush { high_card: Seven },
+            [Six, Five, Four, Three, Two, _, _] => StraightFlush { high_card: Six },
+            [_, Six, Five, Four, Three, Two, _] => StraightFlush { high_card: Six },
+            [_, _, Six, Five, Four, Three, Two] => StraightFlush { high_card: Six },
+            [Ace, _, _, Five, Four, Three, Two] => StraightFlush { high_card: Five },
+            [rank1, rank2, rank3, rank4, rank5, _, _] => Flush { flush: (rank1, rank2, rank3, rank4, rank5) },
+        },
+        [Spades, Spades, Spades, Spades, Spades, Spades, _]
+        | [Hearts, Hearts, Hearts, Hearts, Hearts, Hearts, _]
+        | [Diamonds, Diamonds, Diamonds, Diamonds, Diamonds, Diamonds, _] => {
             match [cards[0].rank, cards[1].rank, cards[2].rank, cards[3].rank, cards[4].rank, cards[5].rank] {
                 [Ace, King, Queen, Jack, Ten, _] => RoyalFlush,
-                [King, Queen, Jack, Ten, Nine, _] => StraightFlush{high_card: King},
-                [Queen, Jack, Ten, Nine, Eight, _] => StraightFlush{high_card: Queen},
-                [_, Queen, Jack, Ten, Nine, Eight] => StraightFlush{high_card: Queen},
-                [Jack, Ten, Nine, Eight, Seven, _] => StraightFlush{high_card: Jack},
-                [_, Jack, Ten, Nine, Eight, Seven] => StraightFlush{high_card: Jack},
-                [Ten, Nine, Eight, Seven, Six, _] => StraightFlush{high_card: Ten},
-                [_, Ten, Nine, Eight, Seven, Six] => StraightFlush{high_card: Ten},
-                [Nine, Eight, Seven, Six, Five, _] => StraightFlush{high_card: Nine},
-                [_, Nine, Eight, Seven, Six, Five] => StraightFlush{high_card: Nine},
-                [Eight, Seven, Six, Five, Four, _] => StraightFlush{high_card: Eight},
-                [_, Eight, Seven, Six, Five, Four] => StraightFlush{high_card: Eight},
-                [Seven, Six, Five, Four, Three, _] => StraightFlush{high_card: Seven},
-                [_, Seven, Six, Five, Four, Three] => StraightFlush{high_card: Seven},
-                [Six, Five, Four, Three, Two, _] => StraightFlush{high_card: Six},
-                [_, Six, Five, Four, Three, Two] => StraightFlush{high_card: Six},
-                [Ace, _, Five, Four, Three, Two] => StraightFlush{high_card: Five},
-                [rank1, rank2, rank3, rank4, rank5, _] => Flush{flush: (rank1, rank2, rank3, rank4, rank5)},
-            }
-        [_, Hearts, Hearts, Hearts, Hearts, Hearts, Hearts] |
-        [_, Diamonds, Diamonds, Diamonds, Diamonds, Diamonds, Diamonds] |
-        [_, Clubs, Clubs, Clubs, Clubs, Clubs, Clubs] =>
-            match [cards[1].rank, cards[2].rank, cards[3].rank, cards[4].rank, cards[5].rank, cards[6].rank] {
-                [Ace, King, Queen, Jack, Ten, _] => RoyalFlush,
-                [King, Queen, Jack, Ten, Nine, _] => StraightFlush{high_card: King},
-                [Queen, Jack, Ten, Nine, Eight, _] => StraightFlush{high_card: Queen},
-                [_, Queen, Jack, Ten, Nine, Eight] => StraightFlush{high_card: Queen},
-                [Jack, Ten, Nine, Eight, Seven, _] => StraightFlush{high_card: Jack},
-                [_, Jack, Ten, Nine, Eight, Seven] => StraightFlush{high_card: Jack},
-                [Ten, Nine, Eight, Seven, Six, _] => StraightFlush{high_card: Ten},
-                [_, Ten, Nine, Eight, Seven, Six] => StraightFlush{high_card: Ten},
-                [Nine, Eight, Seven, Six, Five, _] => StraightFlush{high_card: Nine},
-                [_, Nine, Eight, Seven, Six, Five] => StraightFlush{high_card: Nine},
-                [Eight, Seven, Six, Five, Four, _] => StraightFlush{high_card: Eight},
-                [_, Eight, Seven, Six, Five, Four] => StraightFlush{high_card: Eight},
-                [Seven, Six, Five, Four, Three, _] => StraightFlush{high_card: Seven},
-                [_, Seven, Six, Five, Four, Three] => StraightFlush{high_card: Seven},
-                [Six, Five, Four, Three, Two, _] => StraightFlush{high_card: Six},
-                [_, Six, Five, Four, Three, Two] => StraightFlush{high_card: Six},
-                [Ace, _, Five, Four, Three, Two] => StraightFlush{high_card: Five},
-                [rank1, rank2, rank3, rank4, rank5, _] => Flush{flush: (rank1, rank2, rank3, rank4, rank5)},
+                [King, Queen, Jack, Ten, Nine, _] => StraightFlush { high_card: King },
+                [Queen, Jack, Ten, Nine, Eight, _] => StraightFlush { high_card: Queen },
+                [_, Queen, Jack, Ten, Nine, Eight] => StraightFlush { high_card: Queen },
+                [Jack, Ten, Nine, Eight, Seven, _] => StraightFlush { high_card: Jack },
+                [_, Jack, Ten, Nine, Eight, Seven] => StraightFlush { high_card: Jack },
+                [Ten, Nine, Eight, Seven, Six, _] => StraightFlush { high_card: Ten },
+                [_, Ten, Nine, Eight, Seven, Six] => StraightFlush { high_card: Ten },
+                [Nine, Eight, Seven, Six, Five, _] => StraightFlush { high_card: Nine },
+                [_, Nine, Eight, Seven, Six, Five] => StraightFlush { high_card: Nine },
+                [Eight, Seven, Six, Five, Four, _] => StraightFlush { high_card: Eight },
+                [_, Eight, Seven, Six, Five, Four] => StraightFlush { high_card: Eight },
+                [Seven, Six, Five, Four, Three, _] => StraightFlush { high_card: Seven },
+                [_, Seven, Six, Five, Four, Three] => StraightFlush { high_card: Seven },
+                [Six, Five, Four, Three, Two, _] => StraightFlush { high_card: Six },
+                [_, Six, Five, Four, Three, Two] => StraightFlush { high_card: Six },
+                [Ace, _, Five, Four, Three, Two] => StraightFlush { high_card: Five },
+                [rank1, rank2, rank3, rank4, rank5, _] => Flush { flush: (rank1, rank2, rank3, rank4, rank5) },
             }
-        [Spades, Spades, Spades, Spades, Spades, _, _] |
-        [Hearts, Hearts, Hearts, Hearts, Hearts, _, _] |
-        [Diamonds, Diamonds, Diamonds, Diamonds, Diamonds, _, _] =>
-            match [cards[0].rank, cards[1].rank, cards[2].rank, cards[3].rank, cards[4].rank] {
-                [Ace, King, Queen, Jack, Ten] => RoyalFlush,
-                [King, Queen, Jack, Ten, Nine] => StraightFlush{high_card: King},
-                [Queen, Jack, Ten, Nine, Eight] => StraightFlush{high_card: Queen},
-                [Jack, Ten, Nine, Eight, Seven] => StraightFlush{high_card: Jack},
-                [Ten, Nine, Eight, Seven, Six] => StraightFlush{high_card: Ten},
-                [Nine, Eight, Seven, Six, Five] => StraightFlush{high_card: Nine},
-                [Eight, Seven, Six, Five, Four] => StraightFlush{high_card: Eight},
-                [Seven, Six, Five, Four, Three] => StraightFlush{high_card: Seven},
-                [Six, Five, Four, Three, Two] => StraightFlush{high_card: Six},
-                [Ace, Five, Four, Three, Two] => StraightFlush{high_card: Five},
-                [rank1, rank2, rank3, rank4, rank5] => Flush{flush: (rank1, rank2, rank3, rank4, rank5)},
-            }
-        [_, Hearts, Hearts, Hearts, Hearts, Hearts, _] |
-        [_, Diamonds, Diamonds, Diamonds, Diamonds, Diamonds, _] =>
+        }
+        [_, Hearts, Hearts, Hearts, Hearts, Hearts, Hearts]
+        | [_, Diamonds, Diamonds, Diamonds, Diamonds, Diamonds, Diamonds]
+        | [_, Clubs, Clubs, Clubs, Clubs, Clubs, Clubs] => match [cards[1].rank, cards[2].rank, cards[3].rank, cards[4].rank, cards[5].rank, cards[6].rank] {
+            [Ace, King, Queen, Jack, Ten, _] => RoyalFlush,
+            [King, Queen, Jack, Ten, Nine, _] => StraightFlush { high_card: King },
+            [Queen, Jack, Ten, Nine, Eight, _] => StraightFlush { high_card: Queen },
+            [_, Queen, Jack, Ten, Nine, Eight] => StraightFlush { high_card: Queen },
+            [Jack, Ten, Nine, Eight, Seven, _] => StraightFlush { high_card: Jack },
+            [_, Jack, Ten, Nine, Eight, Seven] => StraightFlush { high_card: Jack },
+            [Ten, Nine, Eight, Seven, Six, _] => StraightFlush { high_card: Ten },
+            [_, Ten, Nine, Eight, Seven, Six] => StraightFlush { high_card: Ten },
+            [Nine, Eight, Seven, Six, Five, _] => StraightFlush { high_card: Nine },
+            [_, Nine, Eight, Seven, Six, Five] => StraightFlush { high_card: Nine },
+            [Eight, Seven, Six, Five, Four, _] => StraightFlush { high_card: Eight },
+            [_, Eight, Seven, Six, Five, Four] => StraightFlush { high_card: Eight },
+            [Seven, Six, Five, Four, Three, _] => StraightFlush { high_card: Seven },
+            [_, Seven, Six, Five, Four, Three] => StraightFlush { high_card: Seven },
+            [Six, Five, Four, Three, Two, _] => StraightFlush { high_card: Six },
+            [_, Six, Five, Four, Three, Two] => StraightFlush { high_card: Six },
+            [Ace, _, Five, Four, Three, Two] => StraightFlush { high_card: Five },
+            [rank1, rank2, rank3, rank4, rank5, _] => Flush { flush: (rank1, rank2, rank3, rank4, rank5) },
+        },
+        [Spades, Spades, Spades, Spades, Spades, _, _]
+        | [Hearts, Hearts, Hearts, Hearts, Hearts, _, _]
+        | [Diamonds, Diamonds, Diamonds, Diamonds, Diamonds, _, _] => match [cards[0].rank, cards[1].rank, cards[2].rank, cards[3].rank, cards[4].rank] {
+            [Ace, King, Queen, Jack, Ten] => RoyalFlush,
+            [King, Queen, Jack, Ten, Nine] => StraightFlush { high_card: King },
+            [Queen, Jack, Ten, Nine, Eight] => StraightFlush { high_card: Queen },
+            [Jack, Ten, Nine, Eight, Seven] => StraightFlush { high_card: Jack },
+            [Ten, Nine, Eight, Seven, Six] => StraightFlush { high_card: Ten },
+            [Nine, Eight, Seven, Six, Five] => StraightFlush { high_card: Nine },
+            [Eight, Seven, Six, Five, Four] => StraightFlush { high_card: Eight },
+            [Seven, Six, Five, Four, Three] => StraightFlush { high_card: Seven },
+            [Six, Five, Four, Three, Two] => StraightFlush { high_card: Six },
+            [Ace, Five, Four, Three, Two] => StraightFlush { high_card: Five },
+            [rank1, rank2, rank3, rank4, rank5] => Flush { flush: (rank1, rank2, rank3, rank4, rank5) },
+        },
+        [_, Hearts, Hearts, Hearts, Hearts, Hearts, _] | [_, Diamonds, Diamonds, Diamonds, Diamonds, Diamonds, _] => {
             match [cards[1].rank, cards[2].rank, cards[3].rank, cards[4].rank, cards[5].rank] {
                 [Ace, King, Queen, Jack, Ten] => RoyalFlush,
-                [King, Queen, Jack, Ten, Nine] => StraightFlush{high_card: King},
-                [Queen, Jack, Ten, Nine, Eight] => StraightFlush{high_card: Queen},
-                [Jack, Ten, Nine, Eight, Seven] => StraightFlush{high_card: Jack},
-                [Ten, Nine, Eight, Seven, Six] => StraightFlush{high_card: Ten},
-                [Nine, Eight, Seven, Six, Five] => StraightFlush{high_card: Nine},
-                [Eight, Seven, Six, Five, Four] => StraightFlush{high_card: Eight},
-                [Seven, Six, Five, Four, Three] => StraightFlush{high_card: Seven},
-                [Six, Five, Four, Three, Two] => StraightFlush{high_card: Six},
-                [Ace, Five, Four, Three, Two] => StraightFlush{high_card: Five},
-                [rank1, rank2, rank3, rank4, rank5] => Flush{flush: (rank1, rank2, rank3, rank4, rank5)},
-            }
-        [_, _, Hearts, Hearts, Hearts, Hearts, Hearts] |
-        [_, _, Diamonds, Diamonds, Diamonds, Diamonds, Diamonds] |
-        [_, _, Clubs, Clubs, Clubs, Clubs, Clubs] =>
-            match [cards[2].rank, cards[3].rank, cards[4].rank, cards[5].rank, cards[6].rank] {
-                [Ace, King, Queen, Jack, Ten] => RoyalFlush,
-                [King, Queen, Jack, Ten, Nine] => StraightFlush{high_card: King},
-                [Queen, Jack, Ten, Nine, Eight] => StraightFlush{high_card: Queen},
-                [Jack, Ten, Nine, Eight, Seven] => StraightFlush{high_card: Jack},
-                [Ten, Nine, Eight, Seven, Six] => StraightFlush{high_card: Ten},
-                [Nine, Eight, Seven, Six, Five] => StraightFlush{high_card: Nine},
-                [Eight, Seven, Six, Five, Four] => StraightFlush{high_card: Eight},
-                [Seven, Six, Five, Four, Three] => StraightFlush{high_card: Seven},
-                [Six, Five, Four, Three, Two] => StraightFlush{high_card: Six},
-                [Ace, Five, Four, Three, Two] => StraightFlush{high_card: Five},
-                [rank1, rank2, rank3, rank4, rank5] => Flush{flush: (rank1, rank2, rank3, rank4, rank5)},
-            }
-        [_, _, _, _, _, _, _] =>
-            match ranks {
-                [quad1, quad2, quad3, quad4, kicker, _, _] if quad1 == quad2 && quad2 == quad3 && quad3 == quad4 => FourOfAKind{quads: quad1, kicker: kicker},
-                [kicker, quad1, quad2, quad3, quad4, _, _] if quad1 == quad2 && quad2 == quad3 && quad3 == quad4 => FourOfAKind{quads: quad1, kicker: kicker},
-                [kicker, _, quad1, quad2, quad3, quad4, _] if quad1 == quad2 && quad2 == quad3 && quad3 == quad4 => FourOfAKind{quads: quad1, kicker: kicker},
-                [kicker, _, _, quad1, quad2, quad3, quad4] if quad1 == quad2 && quad2 == quad3 && quad3 == quad4 => FourOfAKind{quads: quad1, kicker: kicker},
-
-                [trip1, trip2, trip3, pair1, pair2, _, _] if trip1 == trip2 && trip2 == trip3 && pair1 == pair2 => FullHouse{trips: trip1, pair: pair1},
-                [trip1, trip2, trip3, _, pair1, pair2, _] if trip1 == trip2 && trip2 == trip3 && pair1 == pair2 => FullHouse{trips: trip1, pair: pair1},
-                [trip1, trip2, trip3, _, _, pair1, pair2] if trip1 == trip2 && trip2 == trip3 && pair1 == pair2 => FullHouse{trips: trip1, pair: pair1},
-                [_, trip1, trip2, trip3, pair1, pair2, _] if trip1 == trip2 && trip2 == trip3 && pair1 == pair2 => FullHouse{trips: trip1, pair: pair1},
-                [_, trip1, trip2, trip3, _, pair1, pair2] if trip1 == trip2 && trip2 == trip3 && pair1 == pair2 => FullHouse{trips: trip1, pair: pair1},
-                [pair1, pair2, trip1, trip2, trip3, _, _] if trip1 == trip2 && trip2 == trip3 && pair1 == pair2 => FullHouse{trips: trip1, pair: pair1},
-                [pair1, pair2, _, trip1, trip2, trip3, _] if trip1 == trip2 && trip2 == trip3 && pair1 == pair2 => FullHouse{trips: trip1, pair: pair1},
-                [pair1, pair2, _, _, trip1, trip2, trip3] if trip1 == trip2 && trip2 == trip3 && pair1 == pair2 => FullHouse{trips: trip1, pair: pair1},
-                [_, _, trip1, trip2, trip3, pair1, pair2] if trip1 == trip2 && trip2 == trip3 && pair1 == pair2 => FullHouse{trips: trip1, pair: pair1},
-                [_, pair1, pair2, trip1, trip2, trip3, _] if trip1 == trip2 && trip2 == trip3 && pair1 == pair2 => FullHouse{trips: trip1, pair: pair1},
-                [_, pair1, pair2, _, trip1, trip2, trip3] if trip1 == trip2 && trip2 == trip3 && pair1 == pair2 => FullHouse{trips: trip1, pair: pair1},
-                [_, _, pair1, pair2, trip1, trip2, trip3] if trip1 == trip2 && trip2 == trip3 && pair1 == pair2 => FullHouse{trips: trip1, pair: pair1},
-
-                [Ace, King, Queen, Jack, Ten, _, _] => Straight{high_card: Ace},
-                [Ace, King, Queen, Jack, _, Ten, _] => Straight{high_card: Ace},
-                [Ace, King, Queen, Jack, _, _, Ten] => Straight{high_card: Ace},
-                [Ace, King, Queen, _, Jack, Ten, _] => Straight{high_card: Ace},
-                [Ace, King, Queen, _, Jack, _, Ten] => Straight{high_card: Ace},
-                [Ace, King, Queen, _, _, Jack, Ten] => Straight{high_card: Ace},
-                [Ace, King, _, Queen, Jack, Ten, _] => Straight{high_card: Ace},
-                [Ace, King, _, Queen, Jack, _, Ten] => Straight{high_card: Ace},
-                [Ace, King, _, Queen, _, Jack, Ten] => Straight{high_card: Ace},
-                [Ace, King, _, _, Queen, Jack, Ten] => Straight{high_card: Ace},
-                [Ace, _, King, Queen, Jack, Ten, _] => Straight{high_card: Ace},
-                [Ace, _, King, Queen, Jack, _, Ten] => Straight{high_card: Ace},
-                [Ace, _, King, Queen, _, Jack, Ten] => Straight{high_card: Ace},
-                [Ace, _, King, _, Queen, Jack, Ten] => Straight{high_card: Ace},
-                [Ace, _, _, King, Queen, Jack, Ten] => Straight{high_card: Ace},
-                [_, Ace, King, Queen, Jack, Ten, _] => Straight{high_card: Ace},
-                [_, Ace, King, Queen, Jack, _, Ten] => Straight{high_card: Ace},
-                [_, Ace, King, Queen, _, Jack, Ten] => Straight{high_card: Ace},
-                [_, Ace, King, _, Queen, Jack, Ten] => Straight{high_card: Ace},
-                [_, Ace, _, King, Queen, Jack, Ten] => Straight{high_card: Ace},
-                [_, _, Ace, King, Queen, Jack, Ten] => Straight{high_card: Ace},
-
-                [King, Queen, Jack, Ten, Nine, _, _] => Straight{high_card: King},
-                [King, Queen, Jack, Ten, _, Nine, _] => Straight{high_card: King},
-                [King, Queen, Jack, Ten, _, _, Nine] => Straight{high_card: King},
-                [King, Queen, Jack, _, Ten, Nine, _] => Straight{high_card: King},
-                [King, Queen, Jack, _, Ten, _, Nine] => Straight{high_card: King},
-                [King, Queen, Jack, _, _, Ten, Nine] => Straight{high_card: King},
-                [King, Queen, _, Jack, Ten, Nine, _] => Straight{high_card: King},
-                [King, Queen, _, Jack, Ten, _, Nine] => Straight{high_card: King},
-                [King, Queen, _, Jack, _, Ten, Nine] => Straight{high_card: King},
-                [King, Queen, _, _, Jack, Ten, Nine] => Straight{high_card: King},
-                [King, _, Queen, Jack, Ten, Nine, _] => Straight{high_card: King},
-                [King, _, Queen, Jack, Ten, _, Nine] => Straight{high_card: King},
-                [King, _, Queen, Jack, _, Ten, Nine] => Straight{high_card: King},
-                [King, _, Queen, _, Jack, Ten, Nine] => Straight{high_card: King},
-                [King, _, _, Queen, Jack, Ten, Nine] => Straight{high_card: King},
-                [_, King, Queen, Jack, Ten, Nine, _] => Straight{high_card: King},
-                [_, King, Queen, Jack, Ten, _, Nine] => Straight{high_card: King},
-                [_, King, Queen, Jack, _, Ten, Nine] => Straight{high_card: King},
-                [_, King, Queen, _, Jack, Ten, Nine] => Straight{high_card: King},
-                [_, King, _, Queen, Jack, Ten, Nine] => Straight{high_card: King},
-                [_, _, King, Queen, Jack, Ten, Nine] => Straight{high_card: King},
-
-                [Queen, Jack, Ten, Nine, Eight, _, _] => Straight{high_card: Queen},
-                [Queen, Jack, Ten, Nine, _, Eight, _] => Straight{high_card: Queen},
-                [Queen, Jack, Ten, Nine, _, _, Eight] => Straight{high_card: Queen},
-                [Queen, Jack, Ten, _, Nine, Eight, _] => Straight{high_card: Queen},
-                [Queen, Jack, Ten, _, Nine, _, Eight] => Straight{high_card: Queen},
-                [Queen, Jack, Ten, _, _, Nine, Eight] => Straight{high_card: Queen},
-                [Queen, Jack, _, Ten, Nine, Eight, _] => Straight{high_card: Queen},
-                [Queen, Jack, _, Ten, Nine, _, Eight] => Straight{high_card: Queen},
-                [Queen, Jack, _, Ten, _, Nine, Eight] => Straight{high_card: Queen},
-                [Queen, Jack, _, _, Ten, Nine, Eight] => Straight{high_card: Queen},
-                [Queen, _, Jack, Ten, Nine, Eight, _] => Straight{high_card: Queen},
-                [Queen, _, Jack, Ten, Nine, _, Eight] => Straight{high_card: Queen},
-                [Queen, _, Jack, Ten, _, Nine, Eight] => Straight{high_card: Queen},
-                [Queen, _, Jack, _, Ten, Nine, Eight] => Straight{high_card: Queen},
-                [Queen, _, _, Jack, Ten, Nine, Eight] => Straight{high_card: Queen},
-                [_, Queen, Jack, Ten, Nine, Eight, _] => Straight{high_card: Queen},
-                [_, Queen, Jack, Ten, Nine, _, Eight] => Straight{high_card: Queen},
-                [_, Queen, Jack, Ten, _, Nine, Eight] => Straight{high_card: Queen},
-                [_, Queen, Jack, _, Ten, Nine, Eight] => Straight{high_card: Queen},
-                [_, Queen, _, Jack, Ten, Nine, Eight] => Straight{high_card: Queen},
-                [_, _, Queen, Jack, Ten, Nine, Eight] => Straight{high_card: Queen},
-
-                [Jack, Ten, Nine, Eight, Seven, _, _] => Straight{high_card: Jack},
-                [Jack, Ten, Nine, Eight, _, Seven, _] => Straight{high_card: Jack},
-                [Jack, Ten, Nine, Eight, _, _, Seven] => Straight{high_card: Jack},
-                [Jack, Ten, Nine, _, Eight, Seven, _] => Straight{high_card: Jack},
-                [Jack, Ten, Nine, _, Eight, _, Seven] => Straight{high_card: Jack},
-                [Jack, Ten, Nine, _, _, Eight, Seven] => Straight{high_card: Jack},
-                [Jack, Ten, _, Nine, Eight, Seven, _] => Straight{high_card: Jack},
-                [Jack, Ten, _, Nine, Eight, _, Seven] => Straight{high_card: Jack},
-                [Jack, Ten, _, Nine, _, Eight, Seven] => Straight{high_card: Jack},
-                [Jack, Ten, _, _, Nine, Eight, Seven] => Straight{high_card: Jack},
-                [Jack, _, Ten, Nine, Eight, Seven, _] => Straight{high_card: Jack},
-                [Jack, _, Ten, Nine, Eight, _, Seven] => Straight{high_card: Jack},
-                [Jack, _, Ten, Nine, _, Eight, Seven] => Straight{high_card: Jack},
-                [Jack, _, Ten, _, Nine, Eight, Seven] => Straight{high_card: Jack},
-                [Jack, _, _, Ten, Nine, Eight, Seven] => Straight{high_card: Jack},
-                [_, Jack, Ten, Nine, Eight, Seven, _] => Straight{high_card: Jack},
-                [_, Jack, Ten, Nine, Eight, _, Seven] => Straight{high_card: Jack},
-                [_, Jack, Ten, Nine, _, Eight, Seven] => Straight{high_card: Jack},
-                [_, Jack, Ten, _, Nine, Eight, Seven] => Straight{high_card: Jack},
-                [_, Jack, _, Ten, Nine, Eight, Seven] => Straight{high_card: Jack},
-                [_, _, Jack, Ten, Nine, Eight, Seven] => Straight{high_card: Jack},
-
-                [Ten, Nine, Eight, Seven, Six, _, _] => Straight{high_card: Ten},
-                [Ten, Nine, Eight, Seven, _, Six, _] => Straight{high_card: Ten},
-                [Ten, Nine, Eight, Seven, _, _, Six] => Straight{high_card: Ten},
-                [Ten, Nine, Eight, _, Seven, Six, _] => Straight{high_card: Ten},
-                [Ten, Nine, Eight, _, Seven, _, Six] => Straight{high_card: Ten},
-                [Ten, Nine, Eight, _, _, Seven, Six] => Straight{high_card: Ten},
-                [Ten, Nine, _, Eight, Seven, Six, _] => Straight{high_card: Ten},
-                [Ten, Nine, _, Eight, Seven, _, Six] => Straight{high_card: Ten},
-                [Ten, Nine, _, Eight, _, Seven, Six] => Straight{high_card: Ten},
-                [Ten, Nine, _, _, Eight, Seven, Six] => Straight{high_card: Ten},
-                [Ten, _, Nine, Eight, Seven, Six, _] => Straight{high_card: Ten},
-                [Ten, _, Nine, Eight, Seven, _, Six] => Straight{high_card: Ten},
-                [Ten, _, Nine, Eight, _, Seven, Six] => Straight{high_card: Ten},
-                [Ten, _, Nine, _, Eight, Seven, Six] => Straight{high_card: Ten},
-                [Ten, _, _, Nine, Eight, Seven, Six] => Straight{high_card: Ten},
-                [_, Ten, Nine, Eight, Seven, Six, _] => Straight{high_card: Ten},
-                [_, Ten, Nine, Eight, Seven, _, Six] => Straight{high_card: Ten},
-                [_, Ten, Nine, Eight, _, Seven, Six] => Straight{high_card: Ten},
-                [_, Ten, Nine, _, Eight, Seven, Six] => Straight{high_card: Ten},
-                [_, Ten, _, Nine, Eight, Seven, Six] => Straight{high_card: Ten},
-                [_, _, Ten, Nine, Eight, Seven, Six] => Straight{high_card: Ten},
-
-                [Nine, Eight, Seven, Six, Five, _, _] => Straight{high_card: Nine},
-                [Nine, Eight, Seven, Six, _, Five, _] => Straight{high_card: Nine},
-                [Nine, Eight, Seven, Six, _, _, Five] => Straight{high_card: Nine},
-                [Nine, Eight, Seven, _, Six, Five, _] => Straight{high_card: Nine},
-                [Nine, Eight, Seven, _, Six, _, Five] => Straight{high_card: Nine},
-                [Nine, Eight, Seven, _, _, Six, Five] => Straight{high_card: Nine},
-                [Nine, Eight, _, Seven, Six, Five, _] => Straight{high_card: Nine},
-                [Nine, Eight, _, Seven, Six, _, Five] => Straight{high_card: Nine},
-                [Nine, Eight, _, Seven, _, Six, Five] => Straight{high_card: Nine},
-                [Nine, Eight, _, _, Seven, Six, Five] => Straight{high_card: Nine},
-                [Nine, _, Eight, Seven, Six, Five, _] => Straight{high_card: Nine},
-                [Nine, _, Eight, Seven, Six, _, Five] => Straight{high_card: Nine},
-                [Nine, _, Eight, Seven, _, Six, Five] => Straight{high_card: Nine},
-                [Nine, _, Eight, _, Seven, Six, Five] => Straight{high_card: Nine},
-                [Nine, _, _, Eight, Seven, Six, Five] => Straight{high_card: Nine},
-                [_, Nine, Eight, Seven, Six, Five, _] => Straight{high_card: Nine},
-                [_, Nine, Eight, Seven, Six, _, Five] => Straight{high_card: Nine},
-                [_, Nine, Eight, Seven, _, Six, Five] => Straight{high_card: Nine},
-                [_, Nine, Eight, _, Seven, Six, Five] => Straight{high_card: Nine},
-                [_, Nine, _, Eight, Seven, Six, Five] => Straight{high_card: Nine},
-                [_, _, Nine, Eight, Seven, Six, Five] => Straight{high_card: Nine},
-
-                [Eight, Seven, Six, Five, Four, _, _] => Straight{high_card: Eight},
-                [Eight, Seven, Six, Five, _, Four, _] => Straight{high_card: Eight},
-                [Eight, Seven, Six, Five, _, _, Four] => Straight{high_card: Eight},
-                [Eight, Seven, Six, _, Five, Four, _] => Straight{high_card: Eight},
-                [Eight, Seven, Six, _, Five, _, Four] => Straight{high_card: Eight},
-                [Eight, Seven, Six, _, _, Five, Four] => Straight{high_card: Eight},
-                [Eight, Seven, _, Six, Five, Four, _] => Straight{high_card: Eight},
-                [Eight, Seven, _, Six, Five, _, Four] => Straight{high_card: Eight},
-                [Eight, Seven, _, Six, _, Five, Four] => Straight{high_card: Eight},
-                [Eight, Seven, _, _, Six, Five, Four] => Straight{high_card: Eight},
-                [Eight, _, Seven, Six, Five, Four, _] => Straight{high_card: Eight},
-                [Eight, _, Seven, Six, Five, _, Four] => Straight{high_card: Eight},
-                [Eight, _, Seven, Six, _, Five, Four] => Straight{high_card: Eight},
-                [Eight, _, Seven, _, Six, Five, Four] => Straight{high_card: Eight},
-                [Eight, _, _, Seven, Six, Five, Four] => Straight{high_card: Eight},
-                [_, Eight, Seven, Six, Five, Four, _] => Straight{high_card: Eight},
-                [_, Eight, Seven, Six, Five, _, Four] => Straight{high_card: Eight},
-                [_, Eight, Seven, Six, _, Five, Four] => Straight{high_card: Eight},
-                [_, Eight, Seven, _, Six, Five, Four] => Straight{high_card: Eight},
-                [_, Eight, _, Seven, Six, Five, Four] => Straight{high_card: Eight},
-                [_, _, Eight, Seven, Six, Five, Four] => Straight{high_card: Eight},
-
-                [Seven, Six, Five, Four, Three, _, _] => Straight{high_card: Seven},
-                [Seven, Six, Five, Four, _, Three, _] => Straight{high_card: Seven},
-                [Seven, Six, Five, Four, _, _, Three] => Straight{high_card: Seven},
-                [Seven, Six, Five, _, Four, Three, _] => Straight{high_card: Seven},
-                [Seven, Six, Five, _, Four, _, Three] => Straight{high_card: Seven},
-                [Seven, Six, Five, _, _, Four, Three] => Straight{high_card: Seven},
-                [Seven, Six, _, Five, Four, Three, _] => Straight{high_card: Seven},
-                [Seven, Six, _, Five, Four, _, Three] => Straight{high_card: Seven},
-                [Seven, Six, _, Five, _, Four, Three] => Straight{high_card: Seven},
-                [Seven, Six, _, _, Five, Four, Three] => Straight{high_card: Seven},
-                [Seven, _, Six, Five, Four, Three, _] => Straight{high_card: Seven},
-                [Seven, _, Six, Five, Four, _, Three] => Straight{high_card: Seven},
-                [Seven, _, Six, Five, _, Four, Three] => Straight{high_card: Seven},
-                [Seven, _, Six, _, Five, Four, Three] => Straight{high_card: Seven},
-                [Seven, _, _, Six, Five, Four, Three] => Straight{high_card: Seven},
-                [_, Seven, Six, Five, Four, Three, _] => Straight{high_card: Seven},
-                [_, Seven, Six, Five, Four, _, Three] => Straight{high_card: Seven},
-                [_, Seven, Six, Five, _, Four, Three] => Straight{high_card: Seven},
-                [_, Seven, Six, _, Five, Four, Three] => Straight{high_card: Seven},
-                [_, Seven, _, Six, Five, Four, Three] => Straight{high_card: Seven},
-                [_, _, Seven, Six, Five, Four, Three] => Straight{high_card: Seven},
-
-                [Six, Five, Four, Three, Two, _, _] => Straight{high_card: Six},
-                [Six, Five, Four, Three, _, Two, _] => Straight{high_card: Six},
-                [Six, Five, Four, Three, _, _, Two] => Straight{high_card: Six},
-                [Six, Five, Four, _, Three, Two, _] => Straight{high_card: Six},
-                [Six, Five, Four, _, Three, _, Two] => Straight{high_card: Six},
-                [Six, Five, Four, _, _, Three, Two] => Straight{high_card: Six},
-                [Six, Five, _, Four, Three, Two, _] => Straight{high_card: Six},
-                [Six, Five, _, Four, Three, _, Two] => Straight{high_card: Six},
-                [Six, Five, _, Four, _, Three, Two] => Straight{high_card: Six},
-                [Six, Five, _, _, Four, Three, Two] => Straight{high_card: Six},
-                [Six, _, Five, Four, Three, Two, _] => Straight{high_card: Six},
-                [Six, _, Five, Four, Three, _, Two] => Straight{high_card: Six},
-                [Six, _, Five, Four, _, Three, Two] => Straight{high_card: Six},
-                [Six, _, Five, _, Four, Three, Two] => Straight{high_card: Six},
-                [Six, _, _, Five, Four, Three, Two] => Straight{high_card: Six},
-                [_, Six, Five, Four, Three, Two, _] => Straight{high_card: Six},
-                [_, Six, Five, Four, Three, _, Two] => Straight{high_card: Six},
-                [_, Six, Five, Four, _, Three, Two] => Straight{high_card: Six},
-                [_, Six, Five, _, Four, Three, Two] => Straight{high_card: Six},
-                [_, Six, _, Five, Four, Three, Two] => Straight{high_card: Six},
-                [_, _, Six, Five, Four, Three, Two] => Straight{high_card: Six},
-
-                [Ace, Five, Four, Three, Two, _, _] => Straight{high_card: Five},
-                [Ace, Five, Four, Three, _, Two, _] => Straight{high_card: Five},
-                [Ace, Five, Four, Three, _, _, Two] => Straight{high_card: Five},
-                [Ace, Five, Four, _, Three, Two, _] => Straight{high_card: Five},
-                [Ace, Five, Four, _, Three, _, Two] => Straight{high_card: Five},
-                [Ace, Five, Four, _, _, Three, Two] => Straight{high_card: Five},
-                [Ace, Five, _, Four, Three, Two, _] => Straight{high_card: Five},
-                [Ace, Five, _, Four, Three, _, Two] => Straight{high_card: Five},
-                [Ace, Five, _, Four, _, Three, Two] => Straight{high_card: Five},
-                [Ace, Five, _, _, Four, Three, Two] => Straight{high_card: Five},
-                [Ace, _, Five, Four, Three, Two, _] => Straight{high_card: Five},
-                [Ace, _, Five, Four, Three, _, Two] => Straight{high_card: Five},
-                [Ace, _, Five, Four, _, Three, Two] => Straight{high_card: Five},
-                [Ace, _, Five, _, Four, Three, Two] => Straight{high_card: Five},
-                [Ace, _, _, Five, Four, Three, Two] => Straight{high_card: Five},
-                [_, Ace, Five, Four, Three, Two, _] => Straight{high_card: Five},
-                [_, Ace, Five, Four, Three, _, Two] => Straight{high_card: Five},
-                [_, Ace, Five, Four, _, Three, Two] => Straight{high_card: Five},
-                [_, Ace, Five, _, Four, Three, Two] => Straight{high_card: Five},
-                [_, Ace, _, Five, Four, Three, Two] => Straight{high_card: Five},
-                [_, _, Ace, Five, Four, Three, Two] => Straight{high_card: Five},
-
-                [trip1, trip2, trip3, kicker1, kicker2, _, _] if trip1 == trip2 && trip2 == trip3 => ThreeOfAKind{trips: trip1, kickers: (kicker1, kicker2)},
-                [kicker1, trip1, trip2, trip3, kicker2, _, _] if trip1 == trip2 && trip2 == trip3 => ThreeOfAKind{trips: trip1, kickers: (kicker1, kicker2)},
-                [kicker1, kicker2, trip1, trip2, trip3, _, _] if trip1 == trip2 && trip2 == trip3 => ThreeOfAKind{trips: trip1, kickers: (kicker1, kicker2)},
-                [kicker1, kicker2, _, trip1, trip2, trip3, _] if trip1 == trip2 && trip2 == trip3 => ThreeOfAKind{trips: trip1, kickers: (kicker1, kicker2)},
-                [kicker1, kicker2, _, _, trip1, trip2, trip3] if trip1 == trip2 && trip2 == trip3 => ThreeOfAKind{trips: trip1, kickers: (kicker1, kicker2)},
-
-                [pair1, pair2, pair3, pair4, kicker, _, _] if pair1 == pair2 && pair3 == pair4 => TwoPair{pairs: (pair1, pair3), kicker: kicker},
-                [pair1, pair2, kicker, pair3, pair4, _, _] if pair1 == pair2 && pair3 == pair4 => TwoPair{pairs: (pair1, pair3), kicker: kicker},
-                [pair1, pair2, kicker, _, pair3, pair4, _] if pair1 == pair2 && pair3 == pair4 => TwoPair{pairs: (pair1, pair3), kicker: kicker},
-                [pair1, pair2, kicker, _, _, pair3, pair4] if pair1 == pair2 && pair3 == pair4 => TwoPair{pairs: (pair1, pair3), kicker: kicker},
-                [kicker, pair1, pair2, pair3, pair4, _, _] if pair1 == pair2 && pair3 == pair4 => TwoPair{pairs: (pair1, pair3), kicker: kicker},
-                [kicker, pair1, pair2, _, pair3, pair4, _] if pair1 == pair2 && pair3 == pair4 => TwoPair{pairs: (pair1, pair3), kicker: kicker},
-                [kicker, pair1, pair2, _, _, pair3, pair4] if pair1 == pair2 && pair3 == pair4 => TwoPair{pairs: (pair1, pair3), kicker: kicker},
-                [kicker, _, pair1, pair2, pair3, pair4, _] if pair1 == pair2 && pair3 == pair4 => TwoPair{pairs: (pair1, pair3), kicker: kicker},
-                [kicker, _, pair1, pair2, _, pair3, pair4] if pair1 == pair2 && pair3 == pair4 => TwoPair{pairs: (pair1, pair3), kicker: kicker},
-                [kicker, _, _, pair1, pair2, pair3, pair4] if pair1 == pair2 && pair3 == pair4 => TwoPair{pairs: (pair1, pair3), kicker: kicker},
-
-                [pair1, pair2, kicker1, kicker2, kicker3, _, _] if pair1 == pair2 => Pair{pair: pair1, kickers: (kicker1, kicker2, kicker3)},
-                [kicker1, pair1, pair2, kicker2, kicker3, _, _] if pair1 == pair2 => Pair{pair: pair1, kickers: (kicker1, kicker2, kicker3)},
-                [kicker1, kicker2, pair1, pair2, kicker3, _, _] if pair1 == pair2 => Pair{pair: pair1, kickers: (kicker1, kicker2, kicker3)},
-                [kicker1, kicker2, kicker3, pair1, pair2, _, _] if pair1 == pair2 => Pair{pair: pair1, kickers: (kicker1, kicker2, kicker3)},
-                [kicker1, kicker2, kicker3, _, pair1, pair2, _] if pair1 == pair2 => Pair{pair: pair1, kickers: (kicker1, kicker2, kicker3)},
-                [kicker1, kicker2, kicker3, _, _, pair1, pair2] if pair1 == pair2 => Pair{pair: pair1, kickers: (kicker1, kicker2, kicker3)},
-
-                [rank1, rank2, rank3, rank4, rank5, _, _] => HighCard{kickers: (rank1, rank2, rank3, rank4, rank5)},
+                [King, Queen, Jack, Ten, Nine] => StraightFlush { high_card: King },
+                [Queen, Jack, Ten, Nine, Eight] => StraightFlush { high_card: Queen },
+                [Jack, Ten, Nine, Eight, Seven] => StraightFlush { high_card: Jack },
+                [Ten, Nine, Eight, Seven, Six] => StraightFlush { high_card: Ten },
+                [Nine, Eight, Seven, Six, Five] => StraightFlush { high_card: Nine },
+                [Eight, Seven, Six, Five, Four] => StraightFlush { high_card: Eight },
+                [Seven, Six, Five, Four, Three] => StraightFlush { high_card: Seven },
+                [Six, Five, Four, Three, Two] => StraightFlush { high_card: Six },
+                [Ace, Five, Four, Three, Two] => StraightFlush { high_card: Five },
+                [rank1, rank2, rank3, rank4, rank5] => Flush { flush: (rank1, rank2, rank3, rank4, rank5) },
             }
+        }
+        [_, _, Hearts, Hearts, Hearts, Hearts, Hearts]
+        | [_, _, Diamonds, Diamonds, Diamonds, Diamonds, Diamonds]
+        | [_, _, Clubs, Clubs, Clubs, Clubs, Clubs] => match [cards[2].rank, cards[3].rank, cards[4].rank, cards[5].rank, cards[6].rank] {
+            [Ace, King, Queen, Jack, Ten] => RoyalFlush,
+            [King, Queen, Jack, Ten, Nine] => StraightFlush { high_card: King },
+            [Queen, Jack, Ten, Nine, Eight] => StraightFlush { high_card: Queen },
+            [Jack, Ten, Nine, Eight, Seven] => StraightFlush { high_card: Jack },
+            [Ten, Nine, Eight, Seven, Six] => StraightFlush { high_card: Ten },
+            [Nine, Eight, Seven, Six, Five] => StraightFlush { high_card: Nine },
+            [Eight, Seven, Six, Five, Four] => StraightFlush { high_card: Eight },
+            [Seven, Six, Five, Four, Three] => StraightFlush { high_card: Seven },
+            [Six, Five, Four, Three, Two] => StraightFlush { high_card: Six },
+            [Ace, Five, Four, Three, Two] => StraightFlush { high_card: Five },
+            [rank1, rank2, rank3, rank4, rank5] => Flush { flush: (rank1, rank2, rank3, rank4, rank5) },
+        },
+        [_, _, _, _, _, _, _] => match ranks {
+            [quad1, quad2, quad3, quad4, kicker, _, _] if quad1 == quad2 && quad2 == quad3 && quad3 == quad4 => FourOfAKind { quads: quad1, kicker },
+            [kicker, quad1, quad2, quad3, quad4, _, _] if quad1 == quad2 && quad2 == quad3 && quad3 == quad4 => FourOfAKind { quads: quad1, kicker },
+            [kicker, _, quad1, quad2, quad3, quad4, _] if quad1 == quad2 && quad2 == quad3 && quad3 == quad4 => FourOfAKind { quads: quad1, kicker },
+            [kicker, _, _, quad1, quad2, quad3, quad4] if quad1 == quad2 && quad2 == quad3 && quad3 == quad4 => FourOfAKind { quads: quad1, kicker },
+
+            [trip1, trip2, trip3, pair1, pair2, _, _] if trip1 == trip2 && trip2 == trip3 && pair1 == pair2 => FullHouse { trips: trip1, pair: pair1 },
+            [trip1, trip2, trip3, _, pair1, pair2, _] if trip1 == trip2 && trip2 == trip3 && pair1 == pair2 => FullHouse { trips: trip1, pair: pair1 },
+            [trip1, trip2, trip3, _, _, pair1, pair2] if trip1 == trip2 && trip2 == trip3 && pair1 == pair2 => FullHouse { trips: trip1, pair: pair1 },
+            [_, trip1, trip2, trip3, pair1, pair2, _] if trip1 == trip2 && trip2 == trip3 && pair1 == pair2 => FullHouse { trips: trip1, pair: pair1 },
+            [_, trip1, trip2, trip3, _, pair1, pair2] if trip1 == trip2 && trip2 == trip3 && pair1 == pair2 => FullHouse { trips: trip1, pair: pair1 },
+            [pair1, pair2, trip1, trip2, trip3, _, _] if trip1 == trip2 && trip2 == trip3 && pair1 == pair2 => FullHouse { trips: trip1, pair: pair1 },
+            [pair1, pair2, _, trip1, trip2, trip3, _] if trip1 == trip2 && trip2 == trip3 && pair1 == pair2 => FullHouse { trips: trip1, pair: pair1 },
+            [pair1, pair2, _, _, trip1, trip2, trip3] if trip1 == trip2 && trip2 == trip3 && pair1 == pair2 => FullHouse { trips: trip1, pair: pair1 },
+            [_, _, trip1, trip2, trip3, pair1, pair2] if trip1 == trip2 && trip2 == trip3 && pair1 == pair2 => FullHouse { trips: trip1, pair: pair1 },
+            [_, pair1, pair2, trip1, trip2, trip3, _] if trip1 == trip2 && trip2 == trip3 && pair1 == pair2 => FullHouse { trips: trip1, pair: pair1 },
+            [_, pair1, pair2, _, trip1, trip2, trip3] if trip1 == trip2 && trip2 == trip3 && pair1 == pair2 => FullHouse { trips: trip1, pair: pair1 },
+            [_, _, pair1, pair2, trip1, trip2, trip3] if trip1 == trip2 && trip2 == trip3 && pair1 == pair2 => FullHouse { trips: trip1, pair: pair1 },
+
+            [Ace, King, Queen, Jack, Ten, _, _] => Straight { high_card: Ace },
+            [Ace, King, Queen, Jack, _, Ten, _] => Straight { high_card: Ace },
+            [Ace, King, Queen, Jack, _, _, Ten] => Straight { high_card: Ace },
+            [Ace, King, Queen, _, Jack, Ten, _] => Straight { high_card: Ace },
+            [Ace, King, Queen, _, Jack, _, Ten] => Straight { high_card: Ace },
+            [Ace, King, Queen, _, _, Jack, Ten] => Straight { high_card: Ace },
+            [Ace, King, _, Queen, Jack, Ten, _] => Straight { high_card: Ace },
+            [Ace, King, _, Queen, Jack, _, Ten] => Straight { high_card: Ace },
+            [Ace, King, _, Queen, _, Jack, Ten] => Straight { high_card: Ace },
+            [Ace, King, _, _, Queen, Jack, Ten] => Straight { high_card: Ace },
+            [Ace, _, King, Queen, Jack, Ten, _] => Straight { high_card: Ace },
+            [Ace, _, King, Queen, Jack, _, Ten] => Straight { high_card: Ace },
+            [Ace, _, King, Queen, _, Jack, Ten] => Straight { high_card: Ace },
+            [Ace, _, King, _, Queen, Jack, Ten] => Straight { high_card: Ace },
+            [Ace, _, _, King, Queen, Jack, Ten] => Straight { high_card: Ace },
+            [_, Ace, King, Queen, Jack, Ten, _] => Straight { high_card: Ace },
+            [_, Ace, King, Queen, Jack, _, Ten] => Straight { high_card: Ace },
+            [_, Ace, King, Queen, _, Jack, Ten] => Straight { high_card: Ace },
+            [_, Ace, King, _, Queen, Jack, Ten] => Straight { high_card: Ace },
+            [_, Ace, _, King, Queen, Jack, Ten] => Straight { high_card: Ace },
+            [_, _, Ace, King, Queen, Jack, Ten] => Straight { high_card: Ace },
+
+            [King, Queen, Jack, Ten, Nine, _, _] => Straight { high_card: King },
+            [King, Queen, Jack, Ten, _, Nine, _] => Straight { high_card: King },
+            [King, Queen, Jack, Ten, _, _, Nine] => Straight { high_card: King },
+            [King, Queen, Jack, _, Ten, Nine, _] => Straight { high_card: King },
+            [King, Queen, Jack, _, Ten, _, Nine] => Straight { high_card: King },
+            [King, Queen, Jack, _, _, Ten, Nine] => Straight { high_card: King },
+            [King, Queen, _, Jack, Ten, Nine, _] => Straight { high_card: King },
+            [King, Queen, _, Jack, Ten, _, Nine] => Straight { high_card: King },
+            [King, Queen, _, Jack, _, Ten, Nine] => Straight { high_card: King },
+            [King, Queen, _, _, Jack, Ten, Nine] => Straight { high_card: King },
+            [King, _, Queen, Jack, Ten, Nine, _] => Straight { high_card: King },
+            [King, _, Queen, Jack, Ten, _, Nine] => Straight { high_card: King },
+            [King, _, Queen, Jack, _, Ten, Nine] => Straight { high_card: King },
+            [King, _, Queen, _, Jack, Ten, Nine] => Straight { high_card: King },
+            [King, _, _, Queen, Jack, Ten, Nine] => Straight { high_card: King },
+            [_, King, Queen, Jack, Ten, Nine, _] => Straight { high_card: King },
+            [_, King, Queen, Jack, Ten, _, Nine] => Straight { high_card: King },
+            [_, King, Queen, Jack, _, Ten, Nine] => Straight { high_card: King },
+            [_, King, Queen, _, Jack, Ten, Nine] => Straight { high_card: King },
+            [_, King, _, Queen, Jack, Ten, Nine] => Straight { high_card: King },
+            [_, _, King, Queen, Jack, Ten, Nine] => Straight { high_card: King },
+
+            [Queen, Jack, Ten, Nine, Eight, _, _] => Straight { high_card: Queen },
+            [Queen, Jack, Ten, Nine, _, Eight, _] => Straight { high_card: Queen },
+            [Queen, Jack, Ten, Nine, _, _, Eight] => Straight { high_card: Queen },
+            [Queen, Jack, Ten, _, Nine, Eight, _] => Straight { high_card: Queen },
+            [Queen, Jack, Ten, _, Nine, _, Eight] => Straight { high_card: Queen },
+            [Queen, Jack, Ten, _, _, Nine, Eight] => Straight { high_card: Queen },
+            [Queen, Jack, _, Ten, Nine, Eight, _] => Straight { high_card: Queen },
+            [Queen, Jack, _, Ten, Nine, _, Eight] => Straight { high_card: Queen },
+            [Queen, Jack, _, Ten, _, Nine, Eight] => Straight { high_card: Queen },
+            [Queen, Jack, _, _, Ten, Nine, Eight] => Straight { high_card: Queen },
+            [Queen, _, Jack, Ten, Nine, Eight, _] => Straight { high_card: Queen },
+            [Queen, _, Jack, Ten, Nine, _, Eight] => Straight { high_card: Queen },
+            [Queen, _, Jack, Ten, _, Nine, Eight] => Straight { high_card: Queen },
+            [Queen, _, Jack, _, Ten, Nine, Eight] => Straight { high_card: Queen },
+            [Queen, _, _, Jack, Ten, Nine, Eight] => Straight { high_card: Queen },
+            [_, Queen, Jack, Ten, Nine, Eight, _] => Straight { high_card: Queen },
+            [_, Queen, Jack, Ten, Nine, _, Eight] => Straight { high_card: Queen },
+            [_, Queen, Jack, Ten, _, Nine, Eight] => Straight { high_card: Queen },
+            [_, Queen, Jack, _, Ten, Nine, Eight] => Straight { high_card: Queen },
+            [_, Queen, _, Jack, Ten, Nine, Eight] => Straight { high_card: Queen },
+            [_, _, Queen, Jack, Ten, Nine, Eight] => Straight { high_card: Queen },
+
+            [Jack, Ten, Nine, Eight, Seven, _, _] => Straight { high_card: Jack },
+            [Jack, Ten, Nine, Eight, _, Seven, _] => Straight { high_card: Jack },
+            [Jack, Ten, Nine, Eight, _, _, Seven] => Straight { high_card: Jack },
+            [Jack, Ten, Nine, _, Eight, Seven, _] => Straight { high_card: Jack },
+            [Jack, Ten, Nine, _, Eight, _, Seven] => Straight { high_card: Jack },
+            [Jack, Ten, Nine, _, _, Eight, Seven] => Straight { high_card: Jack },
+            [Jack, Ten, _, Nine, Eight, Seven, _] => Straight { high_card: Jack },
+            [Jack, Ten, _, Nine, Eight, _, Seven] => Straight { high_card: Jack },
+            [Jack, Ten, _, Nine, _, Eight, Seven] => Straight { high_card: Jack },
+            [Jack, Ten, _, _, Nine, Eight, Seven] => Straight { high_card: Jack },
+            [Jack, _, Ten, Nine, Eight, Seven, _] => Straight { high_card: Jack },
+            [Jack, _, Ten, Nine, Eight, _, Seven] => Straight { high_card: Jack },
+            [Jack, _, Ten, Nine, _, Eight, Seven] => Straight { high_card: Jack },
+            [Jack, _, Ten, _, Nine, Eight, Seven] => Straight { high_card: Jack },
+            [Jack, _, _, Ten, Nine, Eight, Seven] => Straight { high_card: Jack },
+            [_, Jack, Ten, Nine, Eight, Seven, _] => Straight { high_card: Jack },
+            [_, Jack, Ten, Nine, Eight, _, Seven] => Straight { high_card: Jack },
+            [_, Jack, Ten, Nine, _, Eight, Seven] => Straight { high_card: Jack },
+            [_, Jack, Ten, _, Nine, Eight, Seven] => Straight { high_card: Jack },
+            [_, Jack, _, Ten, Nine, Eight, Seven] => Straight { high_card: Jack },
+            [_, _, Jack, Ten, Nine, Eight, Seven] => Straight { high_card: Jack },
+
+            [Ten, Nine, Eight, Seven, Six, _, _] => Straight { high_card: Ten },
+            [Ten, Nine, Eight, Seven, _, Six, _] => Straight { high_card: Ten },
+            [Ten, Nine, Eight, Seven, _, _, Six] => Straight { high_card: Ten },
+            [Ten, Nine, Eight, _, Seven, Six, _] => Straight { high_card: Ten },
+            [Ten, Nine, Eight, _, Seven, _, Six] => Straight { high_card: Ten },
+            [Ten, Nine, Eight, _, _, Seven, Six] => Straight { high_card: Ten },
+            [Ten, Nine, _, Eight, Seven, Six, _] => Straight { high_card: Ten },
+            [Ten, Nine, _, Eight, Seven, _, Six] => Straight { high_card: Ten },
+            [Ten, Nine, _, Eight, _, Seven, Six] => Straight { high_card: Ten },
+            [Ten, Nine, _, _, Eight, Seven, Six] => Straight { high_card: Ten },
+            [Ten, _, Nine, Eight, Seven, Six, _] => Straight { high_card: Ten },
+            [Ten, _, Nine, Eight, Seven, _, Six] => Straight { high_card: Ten },
+            [Ten, _, Nine, Eight, _, Seven, Six] => Straight { high_card: Ten },
+            [Ten, _, Nine, _, Eight, Seven, Six] => Straight { high_card: Ten },
+            [Ten, _, _, Nine, Eight, Seven, Six] => Straight { high_card: Ten },
+            [_, Ten, Nine, Eight, Seven, Six, _] => Straight { high_card: Ten },
+            [_, Ten, Nine, Eight, Seven, _, Six] => Straight { high_card: Ten },
+            [_, Ten, Nine, Eight, _, Seven, Six] => Straight { high_card: Ten },
+            [_, Ten, Nine, _, Eight, Seven, Six] => Straight { high_card: Ten },
+            [_, Ten, _, Nine, Eight, Seven, Six] => Straight { high_card: Ten },
+            [_, _, Ten, Nine, Eight, Seven, Six] => Straight { high_card: Ten },
+
+            [Nine, Eight, Seven, Six, Five, _, _] => Straight { high_card: Nine },
+            [Nine, Eight, Seven, Six, _, Five, _] => Straight { high_card: Nine },
+            [Nine, Eight, Seven, Six, _, _, Five] => Straight { high_card: Nine },
+            [Nine, Eight, Seven, _, Six, Five, _] => Straight { high_card: Nine },
+            [Nine, Eight, Seven, _, Six, _, Five] => Straight { high_card: Nine },
+            [Nine, Eight, Seven, _, _, Six, Five] => Straight { high_card: Nine },
+            [Nine, Eight, _, Seven, Six, Five, _] => Straight { high_card: Nine },
+            [Nine, Eight, _, Seven, Six, _, Five] => Straight { high_card: Nine },
+            [Nine, Eight, _, Seven, _, Six, Five] => Straight { high_card: Nine },
+            [Nine, Eight, _, _, Seven, Six, Five] => Straight { high_card: Nine },
+            [Nine, _, Eight, Seven, Six, Five, _] => Straight { high_card: Nine },
+            [Nine, _, Eight, Seven, Six, _, Five] => Straight { high_card: Nine },
+            [Nine, _, Eight, Seven, _, Six, Five] => Straight { high_card: Nine },
+            [Nine, _, Eight, _, Seven, Six, Five] => Straight { high_card: Nine },
+            [Nine, _, _, Eight, Seven, Six, Five] => Straight { high_card: Nine },
+            [_, Nine, Eight, Seven, Six, Five, _] => Straight { high_card: Nine },
+            [_, Nine, Eight, Seven, Six, _, Five] => Straight { high_card: Nine },
+            [_, Nine, Eight, Seven, _, Six, Five] => Straight { high_card: Nine },
+            [_, Nine, Eight, _, Seven, Six, Five] => Straight { high_card: Nine },
+            [_, Nine, _, Eight, Seven, Six, Five] => Straight { high_card: Nine },
+            [_, _, Nine, Eight, Seven, Six, Five] => Straight { high_card: Nine },
+
+            [Eight, Seven, Six, Five, Four, _, _] => Straight { high_card: Eight },
+            [Eight, Seven, Six, Five, _, Four, _] => Straight { high_card: Eight },
+            [Eight, Seven, Six, Five, _, _, Four] => Straight { high_card: Eight },
+            [Eight, Seven, Six, _, Five, Four, _] => Straight { high_card: Eight },
+            [Eight, Seven, Six, _, Five, _, Four] => Straight { high_card: Eight },
+            [Eight, Seven, Six, _, _, Five, Four] => Straight { high_card: Eight },
+            [Eight, Seven, _, Six, Five, Four, _] => Straight { high_card: Eight },
+            [Eight, Seven, _, Six, Five, _, Four] => Straight { high_card: Eight },
+            [Eight, Seven, _, Six, _, Five, Four] => Straight { high_card: Eight },
+            [Eight, Seven, _, _, Six, Five, Four] => Straight { high_card: Eight },
+            [Eight, _, Seven, Six, Five, Four, _] => Straight { high_card: Eight },
+            [Eight, _, Seven, Six, Five, _, Four] => Straight { high_card: Eight },
+            [Eight, _, Seven, Six, _, Five, Four] => Straight { high_card: Eight },
+            [Eight, _, Seven, _, Six, Five, Four] => Straight { high_card: Eight },
+            [Eight, _, _, Seven, Six, Five, Four] => Straight { high_card: Eight },
+            [_, Eight, Seven, Six, Five, Four, _] => Straight { high_card: Eight },
+            [_, Eight, Seven, Six, Five, _, Four] => Straight { high_card: Eight },
+            [_, Eight, Seven, Six, _, Five, Four] => Straight { high_card: Eight },
+            [_, Eight, Seven, _, Six, Five, Four] => Straight { high_card: Eight },
+            [_, Eight, _, Seven, Six, Five, Four] => Straight { high_card: Eight },
+            [_, _, Eight, Seven, Six, Five, Four] => Straight { high_card: Eight },
+
+            [Seven, Six, Five, Four, Three, _, _] => Straight { high_card: Seven },
+            [Seven, Six, Five, Four, _, Three, _] => Straight { high_card: Seven },
+            [Seven, Six, Five, Four, _, _, Three] => Straight { high_card: Seven },
+            [Seven, Six, Five, _, Four, Three, _] => Straight { high_card: Seven },
+            [Seven, Six, Five, _, Four, _, Three] => Straight { high_card: Seven },
+            [Seven, Six, Five, _, _, Four, Three] => Straight { high_card: Seven },
+            [Seven, Six, _, Five, Four, Three, _] => Straight { high_card: Seven },
+            [Seven, Six, _, Five, Four, _, Three] => Straight { high_card: Seven },
+            [Seven, Six, _, Five, _, Four, Three] => Straight { high_card: Seven },
+            [Seven, Six, _, _, Five, Four, Three] => Straight { high_card: Seven },
+            [Seven, _, Six, Five, Four, Three, _] => Straight { high_card: Seven },
+            [Seven, _, Six, Five, Four, _, Three] => Straight { high_card: Seven },
+            [Seven, _, Six, Five, _, Four, Three] => Straight { high_card: Seven },
+            [Seven, _, Six, _, Five, Four, Three] => Straight { high_card: Seven },
+            [Seven, _, _, Six, Five, Four, Three] => Straight { high_card: Seven },
+            [_, Seven, Six, Five, Four, Three, _] => Straight { high_card: Seven },
+            [_, Seven, Six, Five, Four, _, Three] => Straight { high_card: Seven },
+            [_, Seven, Six, Five, _, Four, Three] => Straight { high_card: Seven },
+            [_, Seven, Six, _, Five, Four, Three] => Straight { high_card: Seven },
+            [_, Seven, _, Six, Five, Four, Three] => Straight { high_card: Seven },
+            [_, _, Seven, Six, Five, Four, Three] => Straight { high_card: Seven },
+
+            [Six, Five, Four, Three, Two, _, _] => Straight { high_card: Six },
+            [Six, Five, Four, Three, _, Two, _] => Straight { high_card: Six },
+            [Six, Five, Four, Three, _, _, Two] => Straight { high_card: Six },
+            [Six, Five, Four, _, Three, Two, _] => Straight { high_card: Six },
+            [Six, Five, Four, _, Three, _, Two] => Straight { high_card: Six },
+            [Six, Five, Four, _, _, Three, Two] => Straight { high_card: Six },
+            [Six, Five, _, Four, Three, Two, _] => Straight { high_card: Six },
+            [Six, Five, _, Four, Three, _, Two] => Straight { high_card: Six },
+            [Six, Five, _, Four, _, Three, Two] => Straight { high_card: Six },
+            [Six, Five, _, _, Four, Three, Two] => Straight { high_card: Six },
+            [Six, _, Five, Four, Three, Two, _] => Straight { high_card: Six },
+            [Six, _, Five, Four, Three, _, Two] => Straight { high_card: Six },
+            [Six, _, Five, Four, _, Three, Two] => Straight { high_card: Six },
+            [Six, _, Five, _, Four, Three, Two] => Straight { high_card: Six },
+            [Six, _, _, Five, Four, Three, Two] => Straight { high_card: Six },
+            [_, Six, Five, Four, Three, Two, _] => Straight { high_card: Six },
+            [_, Six, Five, Four, Three, _, Two] => Straight { high_card: Six },
+            [_, Six, Five, Four, _, Three, Two] => Straight { high_card: Six },
+            [_, Six, Five, _, Four, Three, Two] => Straight { high_card: Six },
+            [_, Six, _, Five, Four, Three, Two] => Straight { high_card: Six },
+            [_, _, Six, Five, Four, Three, Two] => Straight { high_card: Six },
+
+            [Ace, Five, Four, Three, Two, _, _] => Straight { high_card: Five },
+            [Ace, Five, Four, Three, _, Two, _] => Straight { high_card: Five },
+            [Ace, Five, Four, Three, _, _, Two] => Straight { high_card: Five },
+            [Ace, Five, Four, _, Three, Two, _] => Straight { high_card: Five },
+            [Ace, Five, Four, _, Three, _, Two] => Straight { high_card: Five },
+            [Ace, Five, Four, _, _, Three, Two] => Straight { high_card: Five },
+            [Ace, Five, _, Four, Three, Two, _] => Straight { high_card: Five },
+            [Ace, Five, _, Four, Three, _, Two] => Straight { high_card: Five },
+            [Ace, Five, _, Four, _, Three, Two] => Straight { high_card: Five },
+            [Ace, Five, _, _, Four, Three, Two] => Straight { high_card: Five },
+            [Ace, _, Five, Four, Three, Two, _] => Straight { high_card: Five },
+            [Ace, _, Five, Four, Three, _, Two] => Straight { high_card: Five },
+            [Ace, _, Five, Four, _, Three, Two] => Straight { high_card: Five },
+            [Ace, _, Five, _, Four, Three, Two] => Straight { high_card: Five },
+            [Ace, _, _, Five, Four, Three, Two] => Straight { high_card: Five },
+            [_, Ace, Five, Four, Three, Two, _] => Straight { high_card: Five },
+            [_, Ace, Five, Four, Three, _, Two] => Straight { high_card: Five },
+            [_, Ace, Five, Four, _, Three, Two] => Straight { high_card: Five },
+            [_, Ace, Five, _, Four, Three, Two] => Straight { high_card: Five },
+            [_, Ace, _, Five, Four, Three, Two] => Straight { high_card: Five },
+            [_, _, Ace, Five, Four, Three, Two] => Straight { high_card: Five },
+
+            [trip1, trip2, trip3, kicker1, kicker2, _, _] if trip1 == trip2 && trip2 == trip3 => ThreeOfAKind { trips: trip1, kickers: (kicker1, kicker2) },
+            [kicker1, trip1, trip2, trip3, kicker2, _, _] if trip1 == trip2 && trip2 == trip3 => ThreeOfAKind { trips: trip1, kickers: (kicker1, kicker2) },
+            [kicker1, kicker2, trip1, trip2, trip3, _, _] if trip1 == trip2 && trip2 == trip3 => ThreeOfAKind { trips: trip1, kickers: (kicker1, kicker2) },
+            [kicker1, kicker2, _, trip1, trip2, trip3, _] if trip1 == trip2 && trip2 == trip3 => ThreeOfAKind { trips: trip1, kickers: (kicker1, kicker2) },
+            [kicker1, kicker2, _, _, trip1, trip2, trip3] if trip1 == trip2 && trip2 == trip3 => ThreeOfAKind { trips: trip1, kickers: (kicker1, kicker2) },
+
+            [pair1, pair2, pair3, pair4, kicker, _, _] if pair1 == pair2 && pair3 == pair4 => TwoPair { pairs: (pair1, pair3), kicker },
+            [pair1, pair2, kicker, pair3, pair4, _, _] if pair1 == pair2 && pair3 == pair4 => TwoPair { pairs: (pair1, pair3), kicker },
+            [pair1, pair2, kicker, _, pair3, pair4, _] if pair1 == pair2 && pair3 == pair4 => TwoPair { pairs: (pair1, pair3), kicker },
+            [pair1, pair2, kicker, _, _, pair3, pair4] if pair1 == pair2 && pair3 == pair4 => TwoPair { pairs: (pair1, pair3), kicker },
+            [kicker, pair1, pair2, pair3, pair4, _, _] if pair1 == pair2 && pair3 == pair4 => TwoPair { pairs: (pair1, pair3), kicker },
+            [kicker, pair1, pair2, _, pair3, pair4, _] if pair1 == pair2 && pair3 == pair4 => TwoPair { pairs: (pair1, pair3), kicker },
+            [kicker, pair1, pair2, _, _, pair3, pair4] if pair1 == pair2 && pair3 == pair4 => TwoPair { pairs: (pair1, pair3), kicker },
+            [kicker, _, pair1, pair2, pair3, pair4, _] if pair1 == pair2 && pair3 == pair4 => TwoPair { pairs: (pair1, pair3), kicker },
+            [kicker, _, pair1, pair2, _, pair3, pair4] if pair1 == pair2 && pair3 == pair4 => TwoPair { pairs: (pair1, pair3), kicker },
+            [kicker, _, _, pair1, pair2, pair3, pair4] if pair1 == pair2 && pair3 == pair4 => TwoPair { pairs: (pair1, pair3), kicker },
+
+            [pair1, pair2, kicker1, kicker2, kicker3, _, _] if pair1 == pair2 => Pair { pair: pair1, kickers: (kicker1, kicker2, kicker3) },
+            [kicker1, pair1, pair2, kicker2, kicker3, _, _] if pair1 == pair2 => Pair { pair: pair1, kickers: (kicker1, kicker2, kicker3) },
+            [kicker1, kicker2, pair1, pair2, kicker3, _, _] if pair1 == pair2 => Pair { pair: pair1, kickers: (kicker1, kicker2, kicker3) },
+            [kicker1, kicker2, kicker3, pair1, pair2, _, _] if pair1 == pair2 => Pair { pair: pair1, kickers: (kicker1, kicker2, kicker3) },
+            [kicker1, kicker2, kicker3, _, pair1, pair2, _] if pair1 == pair2 => Pair { pair: pair1, kickers: (kicker1, kicker2, kicker3) },
+            [kicker1, kicker2, kicker3, _, _, pair1, pair2] if pair1 == pair2 => Pair { pair: pair1, kickers: (kicker1, kicker2, kicker3) },
+
+            [rank1, rank2, rank3, rank4, rank5, _, _] => HighCard { kickers: (rank1, rank2, rank3, rank4, rank5) },
+        },
     }
 }
 
@@ -486,30 +484,31 @@ impl Odds {
 
 impl Display for Odds {
     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
-        write!(f, "Player 1: {:.02}%, Player 2: {:.02}%, Draw: {:.02}%",
+        write!(
+            f,
+            "Player 1: {:.02}%, Player 2: {:.02}%, Draw: {:.02}%",
             self.player1_probability() * 100.0,
             self.player2_probability() * 100.0,
-            self.draw_probability() * 100.0)
+            self.draw_probability() * 100.0
+        )
     }
 }
 
 pub fn heads_up_odds(player1: [Card; 2], player2: [Card; 2]) -> Odds {
-    let mut odds = Odds {
-        player1_wins: 0,
-        player2_wins: 0,
-        draws: 0,
-    };
-    let cards: Vec<Card> = FIFTY_TWO_CARD_DECK.iter()
+    let mut odds = Odds { player1_wins: 0, player2_wins: 0, draws: 0 };
+    let cards: Vec<Card> = FIFTY_TWO_CARD_DECK
+        .iter()
         .filter(|&&card| card != player1[0])
         .filter(|&&card| card != player1[1])
         .filter(|&&card| card != player2[0])
         .filter(|&&card| card != player2[1])
-        .cloned().collect();
+        .cloned()
+        .collect();
     for i in 0..48 {
-        for j in i+1..48 {
-            for k in j+1..48 {
-                for l in k+1..48 {
-                    for m in l+1..48 {
+        for j in i + 1..48 {
+            for k in j + 1..48 {
+                for l in k + 1..48 {
+                    for m in l + 1..48 {
                         let player1_rank = rank_7_card_hand([player1[0], player1[1], cards[i], cards[j], cards[k], cards[l], cards[m]]);
                         let player2_rank = rank_7_card_hand([player2[0], player2[1], cards[i], cards[j], cards[k], cards[l], cards[m]]);
                         match player1_rank.cmp(&player2_rank) {
@@ -529,17 +528,12 @@ pub fn heads_up_odds(player1: [Card; 2], player2: [Card; 2]) -> Odds {
 mod test {
     use super::*;
 
-    use rand::Rng;
     use rand::seq::SliceRandom;
+    use rand::Rng;
 
     pub fn random_4_card_draw<R: Rng>(rng: &mut R) -> [Card; 4] {
         let mut cards = FIFTY_TWO_CARD_DECK.choose_multiple(rng, 4);
-        [
-            *cards.next().unwrap(),
-            *cards.next().unwrap(),
-            *cards.next().unwrap(),
-            *cards.next().unwrap(),
-        ]
+        [*cards.next().unwrap(), *cards.next().unwrap(), *cards.next().unwrap(), *cards.next().unwrap()]
     }
 
     pub fn random_7_card_draw<R: Rng>(rng: &mut R) -> [Card; 7] {
@@ -588,71 +582,67 @@ mod test {
         let suits = [cards[0].suit, cards[1].suit, cards[2].suit, cards[3].suit, cards[4].suit];
 
         match suits {
-            [Spades, Spades, Spades, Spades, Spades] |
-            [Hearts, Hearts, Hearts, Hearts, Hearts] |
-            [Diamonds, Diamonds, Diamonds, Diamonds, Diamonds] |
-            [Clubs, Clubs, Clubs, Clubs, Clubs] =>
-                match ranks {
-                    [Ace, King, Queen, Jack, Ten] => RoyalFlush,
-                    [King, Queen, Jack, Ten, Nine] => StraightFlush{high_card: King},
-                    [Queen, Jack, Ten, Nine, Eight] => StraightFlush{high_card: Queen},
-                    [Jack, Ten, Nine, Eight, Seven] => StraightFlush{high_card: Jack},
-                    [Ten, Nine, Eight, Seven, Six] => StraightFlush{high_card: Ten},
-                    [Nine, Eight, Seven, Six, Five] => StraightFlush{high_card: Nine},
-                    [Eight, Seven, Six, Five, Four] => StraightFlush{high_card: Eight},
-                    [Seven, Six, Five, Four, Three] => StraightFlush{high_card: Seven},
-                    [Six, Five, Four, Three, Two] => StraightFlush{high_card: Six},
-                    [Ace, Five, Four, Three, Two] => StraightFlush{high_card: Five},
-                    [rank1, rank2, rank3, rank4, rank5] => Flush{flush: (rank1, rank2, rank3, rank4, rank5)},
-                }
-            [_, _, _, _, _] =>
-                match ranks {
-                    [quad1, quad2, quad3, quad4, kicker] if quad1 == quad2 && quad2 == quad3 && quad3 == quad4 => FourOfAKind{quads: quad1, kicker: kicker},
-                    [kicker, quad1, quad2, quad3, quad4] if quad1 == quad2 && quad2 == quad3 && quad3 == quad4 => FourOfAKind{quads: quad1, kicker: kicker},
-                    [trip1, trip2, trip3, pair1, pair2] if trip1 == trip2 && trip2 == trip3 && pair1 == pair2 => FullHouse{trips: trip1, pair: pair1},
-                    [pair1, pair2, trip1, trip2, trip3] if trip1 == trip2 && trip2 == trip3 && pair1 == pair2 => FullHouse{trips: trip1, pair: pair1},
-                    [Ace, King, Queen, Jack, Ten] => Straight{high_card: Ace},
-                    [King, Queen, Jack, Ten, Nine] => Straight{high_card: King},
-                    [Queen, Jack, Ten, Nine, Eight] => Straight{high_card: Queen},
-                    [Jack, Ten, Nine, Eight, Seven] => Straight{high_card: Jack},
-                    [Ten, Nine, Eight, Seven, Six] => Straight{high_card: Ten},
-                    [Nine, Eight, Seven, Six, Five] => Straight{high_card: Nine},
-                    [Eight, Seven, Six, Five, Four] => Straight{high_card: Eight},
-                    [Seven, Six, Five, Four, Three] => Straight{high_card: Seven},
-                    [Six, Five, Four, Three, Two] => Straight{high_card: Six},
-                    [Ace, Five, Four, Three, Two] => Straight{high_card: Five},
-                    [trip1, trip2, trip3, kicker1, kicker2] if trip1 == trip2 && trip2 == trip3 => ThreeOfAKind{trips: trip1, kickers: (kicker1, kicker2)},
-                    [kicker1, trip1, trip2, trip3, kicker2] if trip1 == trip2 && trip2 == trip3 => ThreeOfAKind{trips: trip1, kickers: (kicker1, kicker2)},
-                    [kicker1, kicker2, trip1, trip2, trip3] if trip1 == trip2 && trip2 == trip3 => ThreeOfAKind{trips: trip1, kickers: (kicker1, kicker2)},
-                    [pair1, pair2, pair3, pair4, kicker] if pair1 == pair2 && pair3 == pair4 => TwoPair{pairs: (pair1, pair3), kicker: kicker},
-                    [pair1, pair2, kicker, pair3, pair4] if pair1 == pair2 && pair3 == pair4 => TwoPair{pairs: (pair1, pair3), kicker: kicker},
-                    [kicker, pair1, pair2, pair3, pair4] if pair1 == pair2 && pair3 == pair4 => TwoPair{pairs: (pair1, pair3), kicker: kicker},
-                    [pair1, pair2, kicker1, kicker2, kicker3] if pair1 == pair2 => Pair{pair: pair1, kickers: (kicker1, kicker2, kicker3)},
-                    [kicker1, pair1, pair2, kicker2, kicker3] if pair1 == pair2 => Pair{pair: pair1, kickers: (kicker1, kicker2, kicker3)},
-                    [kicker1, kicker2, pair1, pair2, kicker3] if pair1 == pair2 => Pair{pair: pair1, kickers: (kicker1, kicker2, kicker3)},
-                    [kicker1, kicker2, kicker3, pair1, pair2] if pair1 == pair2 => Pair{pair: pair1, kickers: (kicker1, kicker2, kicker3)},
-                    [rank1, rank2, rank3, rank4, rank5] => HighCard{kickers: (rank1, rank2, rank3, rank4, rank5)},
-                }
+            [Spades, Spades, Spades, Spades, Spades]
+            | [Hearts, Hearts, Hearts, Hearts, Hearts]
+            | [Diamonds, Diamonds, Diamonds, Diamonds, Diamonds]
+            | [Clubs, Clubs, Clubs, Clubs, Clubs] => match ranks {
+                [Ace, King, Queen, Jack, Ten] => RoyalFlush,
+                [King, Queen, Jack, Ten, Nine] => StraightFlush { high_card: King },
+                [Queen, Jack, Ten, Nine, Eight] => StraightFlush { high_card: Queen },
+                [Jack, Ten, Nine, Eight, Seven] => StraightFlush { high_card: Jack },
+                [Ten, Nine, Eight, Seven, Six] => StraightFlush { high_card: Ten },
+                [Nine, Eight, Seven, Six, Five] => StraightFlush { high_card: Nine },
+                [Eight, Seven, Six, Five, Four] => StraightFlush { high_card: Eight },
+                [Seven, Six, Five, Four, Three] => StraightFlush { high_card: Seven },
+                [Six, Five, Four, Three, Two] => StraightFlush { high_card: Six },
+                [Ace, Five, Four, Three, Two] => StraightFlush { high_card: Five },
+                [rank1, rank2, rank3, rank4, rank5] => Flush { flush: (rank1, rank2, rank3, rank4, rank5) },
+            },
+            [_, _, _, _, _] => match ranks {
+                [quad1, quad2, quad3, quad4, kicker] if quad1 == quad2 && quad2 == quad3 && quad3 == quad4 => FourOfAKind { quads: quad1, kicker },
+                [kicker, quad1, quad2, quad3, quad4] if quad1 == quad2 && quad2 == quad3 && quad3 == quad4 => FourOfAKind { quads: quad1, kicker },
+                [trip1, trip2, trip3, pair1, pair2] if trip1 == trip2 && trip2 == trip3 && pair1 == pair2 => FullHouse { trips: trip1, pair: pair1 },
+                [pair1, pair2, trip1, trip2, trip3] if trip1 == trip2 && trip2 == trip3 && pair1 == pair2 => FullHouse { trips: trip1, pair: pair1 },
+                [Ace, King, Queen, Jack, Ten] => Straight { high_card: Ace },
+                [King, Queen, Jack, Ten, Nine] => Straight { high_card: King },
+                [Queen, Jack, Ten, Nine, Eight] => Straight { high_card: Queen },
+                [Jack, Ten, Nine, Eight, Seven] => Straight { high_card: Jack },
+                [Ten, Nine, Eight, Seven, Six] => Straight { high_card: Ten },
+                [Nine, Eight, Seven, Six, Five] => Straight { high_card: Nine },
+                [Eight, Seven, Six, Five, Four] => Straight { high_card: Eight },
+                [Seven, Six, Five, Four, Three] => Straight { high_card: Seven },
+                [Six, Five, Four, Three, Two] => Straight { high_card: Six },
+                [Ace, Five, Four, Three, Two] => Straight { high_card: Five },
+                [trip1, trip2, trip3, kicker1, kicker2] if trip1 == trip2 && trip2 == trip3 => ThreeOfAKind { trips: trip1, kickers: (kicker1, kicker2) },
+                [kicker1, trip1, trip2, trip3, kicker2] if trip1 == trip2 && trip2 == trip3 => ThreeOfAKind { trips: trip1, kickers: (kicker1, kicker2) },
+                [kicker1, kicker2, trip1, trip2, trip3] if trip1 == trip2 && trip2 == trip3 => ThreeOfAKind { trips: trip1, kickers: (kicker1, kicker2) },
+                [pair1, pair2, pair3, pair4, kicker] if pair1 == pair2 && pair3 == pair4 => TwoPair { pairs: (pair1, pair3), kicker },
+                [pair1, pair2, kicker, pair3, pair4] if pair1 == pair2 && pair3 == pair4 => TwoPair { pairs: (pair1, pair3), kicker },
+                [kicker, pair1, pair2, pair3, pair4] if pair1 == pair2 && pair3 == pair4 => TwoPair { pairs: (pair1, pair3), kicker },
+                [pair1, pair2, kicker1, kicker2, kicker3] if pair1 == pair2 => Pair { pair: pair1, kickers: (kicker1, kicker2, kicker3) },
+                [kicker1, pair1, pair2, kicker2, kicker3] if pair1 == pair2 => Pair { pair: pair1, kickers: (kicker1, kicker2, kicker3) },
+                [kicker1, kicker2, pair1, pair2, kicker3] if pair1 == pair2 => Pair { pair: pair1, kickers: (kicker1, kicker2, kicker3) },
+                [kicker1, kicker2, kicker3, pair1, pair2] if pair1 == pair2 => Pair { pair: pair1, kickers: (kicker1, kicker2, kicker3) },
+                [rank1, rank2, rank3, rank4, rank5] => HighCard { kickers: (rank1, rank2, rank3, rank4, rank5) },
+            },
         }
     }
 
     pub fn heads_up_odds_naive(player1: [Card; 2], player2: [Card; 2]) -> Odds {
-        let mut odds = Odds {
-            player1_wins: 0,
-            player2_wins: 0,
-            draws: 0,
-        };
-        let cards: Vec<Card> = FIFTY_TWO_CARD_DECK.iter()
+        let mut odds = Odds { player1_wins: 0, player2_wins: 0, draws: 0 };
+        let cards: Vec<Card> = FIFTY_TWO_CARD_DECK
+            .iter()
             .filter(|&&card| card != player1[0])
             .filter(|&&card| card != player1[1])
             .filter(|&&card| card != player2[0])
             .filter(|&&card| card != player2[1])
-            .cloned().collect();
+            .cloned()
+            .collect();
         for i in 0..48 {
-            for j in i+1..48 {
-                for k in j+1..48 {
-                    for l in k+1..48 {
-                        for m in l+1..48 {
+            for j in i + 1..48 {
+                for k in j + 1..48 {
+                    for l in k + 1..48 {
+                        for m in l + 1..48 {
                             let player1_rank = rank_7_card_hand([player1[0], player1[1], cards[i], cards[j], cards[k], cards[l], cards[m]]);
                             let player2_rank = rank_7_card_hand([player2[0], player2[1], cards[i], cards[j], cards[k], cards[l], cards[m]]);
                             match player1_rank.cmp(&player2_rank) {
@@ -676,66 +666,114 @@ mod test {
 
     #[test]
     fn rank_straight_flush() {
-        assert_eq!(rank_5_card_hand([KING_OF_SPADES, QUEEN_OF_SPADES, JACK_OF_SPADES, TEN_OF_SPADES, NINE_OF_SPADES]), StraightFlush{high_card: King});
-        assert_eq!(rank_5_card_hand([FIVE_OF_CLUBS, FOUR_OF_CLUBS, THREE_OF_CLUBS, TWO_OF_CLUBS, ACE_OF_CLUBS]), StraightFlush{high_card: Five});
+        assert_eq!(rank_5_card_hand([KING_OF_SPADES, QUEEN_OF_SPADES, JACK_OF_SPADES, TEN_OF_SPADES, NINE_OF_SPADES]), StraightFlush { high_card: King });
+        assert_eq!(rank_5_card_hand([FIVE_OF_CLUBS, FOUR_OF_CLUBS, THREE_OF_CLUBS, TWO_OF_CLUBS, ACE_OF_CLUBS]), StraightFlush { high_card: Five });
     }
 
     #[test]
     fn rank_four_of_a_kind() {
-        assert_eq!(rank_5_card_hand([THREE_OF_CLUBS, THREE_OF_DIAMONDS, THREE_OF_HEARTS, THREE_OF_SPADES, ACE_OF_CLUBS]), FourOfAKind{quads: Three, kicker: Ace});
-        assert_eq!(rank_5_card_hand([THREE_OF_CLUBS, TWO_OF_HEARTS, THREE_OF_HEARTS, THREE_OF_SPADES, THREE_OF_DIAMONDS]), FourOfAKind{quads: Three, kicker: Two});
+        assert_eq!(
+            rank_5_card_hand([THREE_OF_CLUBS, THREE_OF_DIAMONDS, THREE_OF_HEARTS, THREE_OF_SPADES, ACE_OF_CLUBS]),
+            FourOfAKind { quads: Three, kicker: Ace }
+        );
+        assert_eq!(
+            rank_5_card_hand([THREE_OF_CLUBS, TWO_OF_HEARTS, THREE_OF_HEARTS, THREE_OF_SPADES, THREE_OF_DIAMONDS]),
+            FourOfAKind { quads: Three, kicker: Two }
+        );
     }
 
     #[test]
     fn rank_full_house() {
-        assert_eq!(rank_5_card_hand([KING_OF_HEARTS, THREE_OF_DIAMONDS, THREE_OF_HEARTS, KING_OF_DIAMONDS, KING_OF_SPADES]), FullHouse{trips: King, pair: Three});
-        assert_eq!(rank_5_card_hand([THREE_OF_SPADES, THREE_OF_DIAMONDS, THREE_OF_HEARTS, KING_OF_DIAMONDS, KING_OF_SPADES]), FullHouse{trips: Three, pair: King});
+        assert_eq!(
+            rank_5_card_hand([KING_OF_HEARTS, THREE_OF_DIAMONDS, THREE_OF_HEARTS, KING_OF_DIAMONDS, KING_OF_SPADES]),
+            FullHouse { trips: King, pair: Three }
+        );
+        assert_eq!(
+            rank_5_card_hand([THREE_OF_SPADES, THREE_OF_DIAMONDS, THREE_OF_HEARTS, KING_OF_DIAMONDS, KING_OF_SPADES]),
+            FullHouse { trips: Three, pair: King }
+        );
     }
 
     #[test]
     fn rank_flush() {
-        assert_eq!(rank_5_card_hand([THREE_OF_SPADES, SEVEN_OF_SPADES, ACE_OF_SPADES, FOUR_OF_SPADES, QUEEN_OF_SPADES]), Flush{flush: (Ace, Queen, Seven, Four, Three)});
-        assert_eq!(rank_5_card_hand([TWO_OF_HEARTS, EIGHT_OF_HEARTS, FIVE_OF_HEARTS, SEVEN_OF_HEARTS, NINE_OF_HEARTS]), Flush{flush: (Nine, Eight, Seven, Five, Two)});
+        assert_eq!(
+            rank_5_card_hand([THREE_OF_SPADES, SEVEN_OF_SPADES, ACE_OF_SPADES, FOUR_OF_SPADES, QUEEN_OF_SPADES]),
+            Flush { flush: (Ace, Queen, Seven, Four, Three) }
+        );
+        assert_eq!(
+            rank_5_card_hand([TWO_OF_HEARTS, EIGHT_OF_HEARTS, FIVE_OF_HEARTS, SEVEN_OF_HEARTS, NINE_OF_HEARTS]),
+            Flush { flush: (Nine, Eight, Seven, Five, Two) }
+        );
     }
 
     #[test]
     fn rank_straight() {
-        assert_eq!(rank_5_card_hand([EIGHT_OF_HEARTS, QUEEN_OF_SPADES, JACK_OF_SPADES, TEN_OF_SPADES, NINE_OF_SPADES]), Straight{high_card: Queen});
-        assert_eq!(rank_5_card_hand([FIVE_OF_CLUBS, FOUR_OF_SPADES, THREE_OF_CLUBS, TWO_OF_HEARTS, ACE_OF_CLUBS]), Straight{high_card: Five});
+        assert_eq!(rank_5_card_hand([EIGHT_OF_HEARTS, QUEEN_OF_SPADES, JACK_OF_SPADES, TEN_OF_SPADES, NINE_OF_SPADES]), Straight { high_card: Queen });
+        assert_eq!(rank_5_card_hand([FIVE_OF_CLUBS, FOUR_OF_SPADES, THREE_OF_CLUBS, TWO_OF_HEARTS, ACE_OF_CLUBS]), Straight { high_card: Five });
     }
 
     #[test]
     fn rank_three_of_a_kind() {
-        assert_eq!(rank_5_card_hand([THREE_OF_CLUBS, THREE_OF_DIAMONDS, SEVEN_OF_CLUBS, THREE_OF_SPADES, ACE_OF_CLUBS]), ThreeOfAKind{trips: Three, kickers: (Ace, Seven)});
-        assert_eq!(rank_5_card_hand([THREE_OF_CLUBS, TWO_OF_HEARTS, THREE_OF_HEARTS, SIX_OF_CLUBS, THREE_OF_DIAMONDS]), ThreeOfAKind{trips: Three, kickers: (Six, Two)});
+        assert_eq!(
+            rank_5_card_hand([THREE_OF_CLUBS, THREE_OF_DIAMONDS, SEVEN_OF_CLUBS, THREE_OF_SPADES, ACE_OF_CLUBS]),
+            ThreeOfAKind { trips: Three, kickers: (Ace, Seven) }
+        );
+        assert_eq!(
+            rank_5_card_hand([THREE_OF_CLUBS, TWO_OF_HEARTS, THREE_OF_HEARTS, SIX_OF_CLUBS, THREE_OF_DIAMONDS]),
+            ThreeOfAKind { trips: Three, kickers: (Six, Two) }
+        );
     }
 
     #[test]
     fn rank_two_pair() {
-        assert_eq!(rank_5_card_hand([THREE_OF_CLUBS, SEVEN_OF_HEARTS, SEVEN_OF_CLUBS, THREE_OF_SPADES, ACE_OF_CLUBS]), TwoPair{pairs: (Seven, Three), kicker: Ace});
-        assert_eq!(rank_5_card_hand([THREE_OF_CLUBS, TWO_OF_HEARTS, SIX_OF_HEARTS, SIX_OF_CLUBS, THREE_OF_DIAMONDS]), TwoPair{pairs: (Six, Three), kicker: Two});
+        assert_eq!(
+            rank_5_card_hand([THREE_OF_CLUBS, SEVEN_OF_HEARTS, SEVEN_OF_CLUBS, THREE_OF_SPADES, ACE_OF_CLUBS]),
+            TwoPair { pairs: (Seven, Three), kicker: Ace }
+        );
+        assert_eq!(
+            rank_5_card_hand([THREE_OF_CLUBS, TWO_OF_HEARTS, SIX_OF_HEARTS, SIX_OF_CLUBS, THREE_OF_DIAMONDS]),
+            TwoPair { pairs: (Six, Three), kicker: Two }
+        );
     }
 
     #[test]
     fn rank_pair() {
-        assert_eq!(rank_5_card_hand([THREE_OF_CLUBS, SEVEN_OF_HEARTS, NINE_OF_HEARTS, THREE_OF_SPADES, ACE_OF_CLUBS]), Pair{pair: Three, kickers: (Ace, Nine, Seven)});
-        assert_eq!(rank_5_card_hand([THREE_OF_CLUBS, TWO_OF_HEARTS, SEVEN_OF_HEARTS, SIX_OF_CLUBS, THREE_OF_DIAMONDS]), Pair{pair: Three, kickers: (Seven, Six, Two)});
+        assert_eq!(
+            rank_5_card_hand([THREE_OF_CLUBS, SEVEN_OF_HEARTS, NINE_OF_HEARTS, THREE_OF_SPADES, ACE_OF_CLUBS]),
+            Pair { pair: Three, kickers: (Ace, Nine, Seven) }
+        );
+        assert_eq!(
+            rank_5_card_hand([THREE_OF_CLUBS, TWO_OF_HEARTS, SEVEN_OF_HEARTS, SIX_OF_CLUBS, THREE_OF_DIAMONDS]),
+            Pair { pair: Three, kickers: (Seven, Six, Two) }
+        );
     }
 
     #[test]
     fn rank_high_card() {
-        assert_eq!(rank_5_card_hand([ACE_OF_SPADES, TWO_OF_HEARTS, SEVEN_OF_HEARTS, SIX_OF_CLUBS, THREE_OF_DIAMONDS]), HighCard{kickers: (Ace, Seven, Six, Three, Two)});
-        assert_eq!(rank_5_card_hand([EIGHT_OF_SPADES, SIX_OF_DIAMONDS, SEVEN_OF_HEARTS, KING_OF_HEARTS, FOUR_OF_CLUBS]), HighCard{kickers: (King, Eight, Seven, Six, Four)});
+        assert_eq!(
+            rank_5_card_hand([ACE_OF_SPADES, TWO_OF_HEARTS, SEVEN_OF_HEARTS, SIX_OF_CLUBS, THREE_OF_DIAMONDS]),
+            HighCard { kickers: (Ace, Seven, Six, Three, Two) }
+        );
+        assert_eq!(
+            rank_5_card_hand([EIGHT_OF_SPADES, SIX_OF_DIAMONDS, SEVEN_OF_HEARTS, KING_OF_HEARTS, FOUR_OF_CLUBS]),
+            HighCard { kickers: (King, Eight, Seven, Six, Four) }
+        );
     }
 
     #[test]
     fn rank_7_card_pair() {
-        assert_eq!(rank_7_card_hand([NINE_OF_CLUBS, TWO_OF_SPADES, SIX_OF_CLUBS, ACE_OF_DIAMONDS, FOUR_OF_HEARTS, THREE_OF_HEARTS, FOUR_OF_CLUBS]), Pair{pair: Four, kickers: (Ace, Nine, Six)});
+        assert_eq!(
+            rank_7_card_hand([NINE_OF_CLUBS, TWO_OF_SPADES, SIX_OF_CLUBS, ACE_OF_DIAMONDS, FOUR_OF_HEARTS, THREE_OF_HEARTS, FOUR_OF_CLUBS]),
+            Pair { pair: Four, kickers: (Ace, Nine, Six) }
+        );
     }
 
     #[test]
     fn rank_7_card_two_pair() {
-        assert_eq!(rank_7_card_hand([SIX_OF_HEARTS, JACK_OF_SPADES, TWO_OF_DIAMONDS, TEN_OF_CLUBS, TWO_OF_CLUBS, SIX_OF_DIAMONDS, FOUR_OF_SPADES]), TwoPair{pairs: (Six, Two), kicker: Jack});
+        assert_eq!(
+            rank_7_card_hand([SIX_OF_HEARTS, JACK_OF_SPADES, TWO_OF_DIAMONDS, TEN_OF_CLUBS, TWO_OF_CLUBS, SIX_OF_DIAMONDS, FOUR_OF_SPADES]),
+            TwoPair { pairs: (Six, Two), kicker: Jack }
+        );
     }
 
     #[test]
index ba3dce0d84a7c2c1b7c65c185677ad901e041021..3ac130b23bf6cb993ceb963e407a1f0640e99155 100644 (file)
@@ -4,8 +4,8 @@ use std::convert::TryInto;
 use itertools::Itertools;
 
 use crate::card::{Card, FIFTY_TWO_CARD_DECK};
-use crate::seats::Seats;
 use crate::rng::{Seed, WaveRng};
+use crate::seats::Seats;
 use crate::username::Username;
 use crate::util::{max::IteratorMaxItems, timestamp::Timestamp};
 
@@ -133,34 +133,27 @@ impl TexasHoldEm {
     }
 
     fn all_bets_are_in(&self) -> bool {
-        self.in_hand.iter().filter(|&&username| self.is_able_to_bet(username))
-            .all(|username| self.bets.contains_key(username))
+        self.in_hand.iter().filter(|&&username| self.is_able_to_bet(username)).all(|username| self.bets.contains_key(username))
     }
 
     fn player_is_all_in(&self, username: Username) -> bool {
         matches!(self.stacks.get(&username), Some(&0) | None)
     }
 
-    fn bets_of_players_who_are_not_all_in(&self) -> impl Iterator<Item=u64> + '_ {
-        self.bets.iter()
-            .filter(move |&(&username, _)| !self.player_is_all_in(username))
-            .map(|(_, &bet)| bet)
+    fn bets_of_players_who_are_not_all_in(&self) -> impl Iterator<Item = u64> + '_ {
+        self.bets.iter().filter(move |&(&username, _)| !self.player_is_all_in(username)).map(|(_, &bet)| bet)
     }
 
     fn all_bets_are_equal(&self) -> bool {
         self.players_able_to_bet() == 0
-            || self.bets_of_players_who_are_not_all_in().all_equal()
-                && self.bets.values().cloned().max() <= self.bets_of_players_who_are_not_all_in().max()
+            || self.bets_of_players_who_are_not_all_in().all_equal() && self.bets.values().cloned().max() <= self.bets_of_players_who_are_not_all_in().max()
     }
 
     fn big_blind_player(&self) -> Option<Username> {
         if self.seats.players_len() == 2 {
-            self.dealer
-                .and_then(|dealer| self.seats.player_after(dealer))
+            self.dealer.and_then(|dealer| self.seats.player_after(dealer))
         } else {
-            self.dealer
-                .and_then(|dealer| self.seats.player_after(dealer))
-                .and_then(|small_blind| self.seats.player_after(small_blind))
+            self.dealer.and_then(|dealer| self.seats.player_after(dealer)).and_then(|small_blind| self.seats.player_after(small_blind))
         }
     }
 
@@ -172,8 +165,7 @@ impl TexasHoldEm {
                 }
             }
         }
-        self.all_bets_are_in() && self.all_bets_are_equal()
-            || self.players_able_to_bet() <= 1 && self.bets.len() == 0
+        self.all_bets_are_in() && self.all_bets_are_equal() || self.players_able_to_bet() <= 1 && self.bets.len() == 0
     }
 
     fn remove_ghosts(&mut self) -> Result<(), ActionError> {
@@ -217,12 +209,10 @@ impl Game for TexasHoldEm {
         self.actions_len
     }
 
-    fn validate_action(&self, UserAction{timestamp, username, action}: UserAction) -> Result<ValidatedUserAction, ActionError> {
+    fn validate_action(&self, UserAction { timestamp, username, action }: UserAction) -> Result<ValidatedUserAction, ActionError> {
         match (self.state, action) {
-            (_, Action::PlayCard{..}) | (_, Action::ChooseTrumps{..}) => {
-                Err(ActionError::InvalidActionForGameType)
-            }
-            (State::NotStarted, Action::Join{seat, chips}) => {
+            (_, Action::PlayCard { .. }) | (_, Action::ChooseTrumps { .. }) => Err(ActionError::InvalidActionForGameType),
+            (State::NotStarted, Action::Join { seat, chips }) => {
                 if self.seats.contains_player(username) {
                     Err(ActionError::AlreadyJoined)
                 } else if self.seats.players_len() > self.settings.max_players as usize {
@@ -234,31 +224,27 @@ impl Game for TexasHoldEm {
                 } else if chips > self.settings.starting_stack {
                     Err(ActionError::StartingStackTooLarge)
                 } else {
-                    Ok(ValidatedUserAction(UserAction{timestamp, username, action: Action::Join{seat, chips}}))
+                    Ok(ValidatedUserAction(UserAction { timestamp, username, action: Action::Join { seat, chips } }))
                 }
             }
-            (State::Completed, Action::Join{..}) => Err(ActionError::GameHasEnded),
-            (_, Action::Join{..}) => Err(ActionError::GameHasStarted),
+            (State::Completed, Action::Join { .. }) => Err(ActionError::GameHasEnded),
+            (_, Action::Join { .. }) => Err(ActionError::GameHasStarted),
             (_, _) if !self.seats.contains_player(username) => Err(ActionError::NotAuthorised),
-            (State::NotStarted, Action::Leave) => {
-                Ok(ValidatedUserAction(UserAction{timestamp, username, action: Action::Leave}))
-            }
+            (State::NotStarted, Action::Leave) => Ok(ValidatedUserAction(UserAction { timestamp, username, action: Action::Leave })),
             (_, _) if !self.seats.contains_player(username) => Err(ActionError::NotAuthorised),
             (State::Completed, _) => Err(ActionError::GameHasEnded),
             (_, Action::Leave) => Err(ActionError::GameHasStarted),
-            (State::Dealing, _) | (State::DealingFlop, _) | (State::DealingTurn, _) | (State::DealingRiver, _) => {
-                Err(ActionError::Dealing)
-            }
-            (_, Action::Fold) | (_, Action::Bet{..}) if !self.in_hand.contains(&username) => Err(ActionError::NotInHand),
-            (_, Action::Fold) | (_, Action::Bet{..}) if self.active != Some(username) => Err(ActionError::OutOfTurn),
+            (State::Dealing, _) | (State::DealingFlop, _) | (State::DealingTurn, _) | (State::DealingRiver, _) => Err(ActionError::Dealing),
+            (_, Action::Fold) | (_, Action::Bet { .. }) if !self.in_hand.contains(&username) => Err(ActionError::NotInHand),
+            (_, Action::Fold) | (_, Action::Bet { .. }) if self.active != Some(username) => Err(ActionError::OutOfTurn),
             (_, Action::Fold) if self.chips_to_call(username) == 0 => Err(ActionError::CannotFold),
-            (_, Action::Fold) => Ok(ValidatedUserAction(UserAction{timestamp, username, action: Action::Fold})),
-            (_, Action::Bet{chips}) => {
+            (_, Action::Fold) => Ok(ValidatedUserAction(UserAction { timestamp, username, action: Action::Fold })),
+            (_, Action::Bet { chips }) => {
                 let stack = self.stack(username);
                 if chips > stack {
                     Err(ActionError::NotEnoughChips)
                 } else if chips == stack {
-                    Ok(ValidatedUserAction(UserAction{timestamp, username, action: Action::Bet{chips}}))
+                    Ok(ValidatedUserAction(UserAction { timestamp, username, action: Action::Bet { chips } }))
                 } else {
                     let to_call = self.chips_to_call(username);
                     let min_raise = self.min_raise();
@@ -267,7 +253,7 @@ impl Game for TexasHoldEm {
                     } else if chips > to_call && chips < to_call + min_raise {
                         Err(ActionError::BetSizeTooSmall)
                     } else {
-                        Ok(ValidatedUserAction(UserAction{timestamp, username, action: Action::Bet{chips}}))
+                        Ok(ValidatedUserAction(UserAction { timestamp, username, action: Action::Bet { chips } }))
                     }
                 }
             }
@@ -275,15 +261,13 @@ impl Game for TexasHoldEm {
         }
     }
 
-    fn take_action(&mut self, ValidatedUserAction(UserAction{timestamp, username, action}): ValidatedUserAction) -> Result<(), ActionError> {
+    fn take_action(&mut self, ValidatedUserAction(UserAction { timestamp, username, action }): ValidatedUserAction) -> Result<(), ActionError> {
         self.actions_len += 1;
         self.last_action_time = Some(timestamp);
         self.rng.advance();
         match (self.state, action) {
-            (_, Action::PlayCard{..}) | (_, Action::ChooseTrumps{..}) => {
-                Err(ActionError::InvalidActionForGameType)
-            }
-            (State::NotStarted, Action::Join{seat, chips}) => {
+            (_, Action::PlayCard { .. }) | (_, Action::ChooseTrumps { .. }) => Err(ActionError::InvalidActionForGameType),
+            (State::NotStarted, Action::Join { seat, chips }) => {
                 self.stacks.insert(username, chips);
                 self.seats.add_player(seat, username)
             }
@@ -327,7 +311,7 @@ impl Game for TexasHoldEm {
                 self.state = State::Dealing;
                 Ok(())
             }
-            (State::Dealing, Action::ReceiveCard{card: Some(card)}) => {
+            (State::Dealing, Action::ReceiveCard { card: Some(card) }) => {
                 self.deck.remove(&card);
                 self.hands.entry(username).or_default().insert(card);
                 if self.hands.values().all(|hand| hand.len() == 2) {
@@ -342,14 +326,14 @@ impl Game for TexasHoldEm {
                 self.receiver = None;
                 Ok(())
             }
-            (State::PostingSmallBlind, Action::PostBlind{chips}) => {
+            (State::PostingSmallBlind, Action::PostBlind { chips }) => {
                 *self.bets.entry(username).or_default() += chips;
                 *self.committed.entry(username).or_default() += chips;
                 *self.stacks.entry(username).or_default() -= chips;
                 self.state = State::PostingBigBlind;
                 Ok(())
             }
-            (State::PostingBigBlind, Action::PostBlind{chips}) => {
+            (State::PostingBigBlind, Action::PostBlind { chips }) => {
                 *self.bets.entry(username).or_default() += chips;
                 *self.committed.entry(username).or_default() += chips;
                 *self.stacks.entry(username).or_default() -= chips;
@@ -357,7 +341,7 @@ impl Game for TexasHoldEm {
                 self.state = State::PreFlopBetting;
                 Ok(())
             }
-            (_, Action::Bet{chips}) => {
+            (_, Action::Bet { chips }) => {
                 *self.bets.entry(username).or_default() += chips;
                 *self.committed.entry(username).or_default() += chips;
                 *self.stacks.entry(username).or_default() -= chips;
@@ -384,15 +368,12 @@ impl Game for TexasHoldEm {
                 }
                 Ok(())
             }
-            (State::DealingFlop, Action::CommunityCard{card}) => {
+            (State::DealingFlop, Action::CommunityCard { card }) => {
                 self.community.insert(card);
                 self.deck.remove(&card);
                 if self.community.len() == 3 {
-                    self.active = if self.betting_round_completed() {
-                        None
-                    } else {
-                        self.seats.player_after_where(username, |username| self.is_able_to_bet(username))
-                    };
+                    self.active =
+                        if self.betting_round_completed() { None } else { self.seats.player_after_where(username, |username| self.is_able_to_bet(username)) };
                     self.state = match self.active {
                         Some(_) => State::PostFlopBetting,
                         None => State::DealingTurn,
@@ -400,35 +381,29 @@ impl Game for TexasHoldEm {
                 }
                 Ok(())
             }
-            (State::DealingTurn, Action::CommunityCard{card}) => {
+            (State::DealingTurn, Action::CommunityCard { card }) => {
                 self.community.insert(card);
                 self.deck.remove(&card);
-                self.active = if self.betting_round_completed() {
-                    None
-                } else {
-                    self.seats.player_after_where(username, |username| self.is_able_to_bet(username))
-                };
+                self.active =
+                    if self.betting_round_completed() { None } else { self.seats.player_after_where(username, |username| self.is_able_to_bet(username)) };
                 self.state = match self.active {
                     Some(_) => State::TurnBetting,
                     None => State::DealingRiver,
                 };
                 Ok(())
             }
-            (State::DealingRiver, Action::CommunityCard{card}) => {
+            (State::DealingRiver, Action::CommunityCard { card }) => {
                 self.community.insert(card);
                 self.deck.remove(&card);
-                self.active = if self.betting_round_completed() {
-                    None
-                } else {
-                    self.seats.player_after_where(username, |username| self.is_able_to_bet(username))
-                };
+                self.active =
+                    if self.betting_round_completed() { None } else { self.seats.player_after_where(username, |username| self.is_able_to_bet(username)) };
                 self.state = match self.active {
                     Some(_) => State::RiverBetting,
                     None => State::Showdown,
                 };
                 Ok(())
             }
-            (_, Action::WinHand{chips, ..}) if chips <= self.pot => {
+            (_, Action::WinHand { chips, .. }) if chips <= self.pot => {
                 self.pot -= chips;
                 *self.stacks.entry(username).or_default() += chips;
                 self.hands.remove(&username);
@@ -448,11 +423,10 @@ impl Game for TexasHoldEm {
         let mut rng = self.rng.clone();
         match self.state {
             State::NotStarted => {
-                if self.seats.players_len() == self.settings.max_players as usize { // TODO
+                if self.seats.players_len() == self.settings.max_players as usize {
+                    // TODO
                     if let Some(username) = rng.choose_from(self.seats.player_set()) {
-                        return DealerAction::TakeAction(
-                            ValidatedUserAction(UserAction{timestamp, username, action: Action::NextToDeal})
-                        );
+                        return DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::NextToDeal }));
                     }
                 }
                 DealerAction::WaitForPlayer
@@ -460,13 +434,9 @@ impl Game for TexasHoldEm {
             State::Dealing => {
                 if let Some(username) = self.receiver {
                     let card = rng.choose_from(&self.deck).cloned();
-                    DealerAction::TakeAction(
-                        ValidatedUserAction(UserAction{timestamp, username, action: Action::ReceiveCard{card}})
-                    )
+                    DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::ReceiveCard { card } }))
                 } else if let Some(username) = self.dealer {
-                    DealerAction::TakeAction(
-                        ValidatedUserAction(UserAction{timestamp, username, action: Action::EndDeal})
-                    )
+                    DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::EndDeal }))
                 } else {
                     error!("Expected to deal a card but there was no dealer: {:#?}", self);
                     DealerAction::Leave
@@ -475,9 +445,7 @@ impl Game for TexasHoldEm {
             State::PostingSmallBlind if self.seats.players_len() == 2 => {
                 if let Some(username) = self.dealer {
                     let chips = self.stack(username).min(self.small_blind);
-                    DealerAction::TakeAction(
-                        ValidatedUserAction(UserAction{timestamp, username, action: Action::PostBlind{chips}})
-                    )
+                    DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::PostBlind { chips } }))
                 } else {
                     error!("There is no player to post the small blind: {:#?}", self);
                     DealerAction::Leave
@@ -486,9 +454,7 @@ impl Game for TexasHoldEm {
             State::PostingSmallBlind => {
                 if let Some(username) = self.dealer.and_then(|dealer| self.seats.player_after(dealer)) {
                     let chips = self.stack(username).min(self.small_blind);
-                    DealerAction::TakeAction(
-                        ValidatedUserAction(UserAction{timestamp, username, action: Action::PostBlind{chips}})
-                    )
+                    DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::PostBlind { chips } }))
                 } else {
                     error!("There is no player to post the small blind: {:#?}", self);
                     DealerAction::Leave
@@ -497,23 +463,18 @@ impl Game for TexasHoldEm {
             State::PostingBigBlind if self.seats.players_len() == 2 => {
                 if let Some(username) = self.dealer.and_then(|dealer| self.seats.player_after(dealer)) {
                     let chips = self.stack(username).min(self.small_blind * 2);
-                    DealerAction::TakeAction(
-                        ValidatedUserAction(UserAction{timestamp, username, action: Action::PostBlind{chips}})
-                    )
+                    DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::PostBlind { chips } }))
                 } else {
                     error!("There is no player to post the big blind: {:#?}", self);
                     DealerAction::Leave
                 }
             }
             State::PostingBigBlind => {
-                if let Some(username) = self.dealer
-                    .and_then(|dealer| self.seats.player_after(dealer))
-                    .and_then(|small_blind| self.seats.player_after(small_blind))
+                if let Some(username) =
+                    self.dealer.and_then(|dealer| self.seats.player_after(dealer)).and_then(|small_blind| self.seats.player_after(small_blind))
                 {
                     let chips = self.stack(username).min(self.small_blind * 2);
-                    DealerAction::TakeAction(
-                        ValidatedUserAction(UserAction{timestamp, username, action: Action::PostBlind{chips}})
-                    )
+                    DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::PostBlind { chips } }))
                 } else {
                     error!("There is no player to post the big blind: {:#?}", self);
                     DealerAction::Leave
@@ -523,37 +484,26 @@ impl Game for TexasHoldEm {
                 if self.in_hand.len() <= 1 {
                     if self.pot > 0 {
                         if let Some(&username) = self.in_hand.iter().next() {
-                            DealerAction::TakeAction(
-                                ValidatedUserAction(UserAction {
-                                    timestamp,
-                                    username,
-                                    action: Action::WinHand {
-                                        chips: self.pot,
-                                        hand: None,
-                                    }
-                                })
-                            )
+                            DealerAction::TakeAction(ValidatedUserAction(UserAction {
+                                timestamp,
+                                username,
+                                action: Action::WinHand { chips: self.pot, hand: None },
+                            }))
                         } else {
                             error!("There is no player to win the pot: {:#?}", self);
                             DealerAction::Leave
                         }
                     } else if self.seats.players_len() == 1 {
                         if let Some(&username) = self.seats.player_set().iter().next() {
-                            DealerAction::TakeAction(
-                                ValidatedUserAction(UserAction{timestamp, username, action: Action::WinGame})
-                            )
+                            DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::WinGame }))
                         } else {
                             error!("There is no player to win the game {:#?}", self);
                             DealerAction::Leave
                         }
                     } else if let Some((&username, _)) = self.stacks.iter().find(|&(_, &stack)| stack == 0) {
-                        DealerAction::TakeAction(
-                            ValidatedUserAction(UserAction{timestamp, username, action: Action::KnockedOut})
-                        )
+                        DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::KnockedOut }))
                     } else if let Some(username) = self.dealer.and_then(|dealer| self.seats.player_after(dealer)) {
-                        DealerAction::TakeAction(
-                            ValidatedUserAction(UserAction{timestamp, username, action: Action::NextToDeal})
-                        )
+                        DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::NextToDeal }))
                     } else {
                         error!("Logic error: no dealer could be chosen: {:#?}", self);
                         DealerAction::Leave
@@ -561,9 +511,7 @@ impl Game for TexasHoldEm {
                 } else if let (Some(last_time), Some(username), Some(timeout)) = (self.last_action_time, self.active, self.settings.action_timeout) {
                     let timeout_time = last_time.plus_millis(timeout);
                     if timestamp >= timeout_time {
-                        DealerAction::TakeAction(
-                            ValidatedUserAction(UserAction{timestamp, username, action: Action::TimeoutFold})
-                        )
+                        DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::TimeoutFold }))
                     } else {
                         DealerAction::WaitUntil(timeout_time)
                     }
@@ -574,9 +522,7 @@ impl Game for TexasHoldEm {
             State::DealingFlop | State::DealingTurn | State::DealingRiver => {
                 if let Some(username) = self.dealer {
                     if let Some(&card) = rng.choose_from(&self.deck) {
-                        DealerAction::TakeAction(
-                            ValidatedUserAction(UserAction{timestamp, username, action: Action::CommunityCard{card}})
-                        )
+                        DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::CommunityCard { card } }))
                     } else {
                         error!("Dealing community card but there are no cards left in deck: {:#?}", self);
                         DealerAction::Leave
@@ -588,29 +534,25 @@ impl Game for TexasHoldEm {
             }
             State::Showdown if self.pot == 0 => {
                 if let Some((&username, _)) = self.stacks.iter().find(|&(_, &stack)| stack == 0) {
-                    DealerAction::TakeAction(
-                        ValidatedUserAction(UserAction{timestamp, username, action: Action::KnockedOut})
-                    )
+                    DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::KnockedOut }))
                 } else if self.seats.players_len() == 1 {
                     if let Some(&username) = self.seats.player_set().iter().next() {
-                        DealerAction::TakeAction(
-                            ValidatedUserAction(UserAction{timestamp, username, action: Action::WinGame})
-                        )
+                        DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::WinGame }))
                     } else {
                         error!("There was no player to win the game: {:#?}", self);
                         DealerAction::Leave
                     }
                 } else if let Some(username) = self.dealer.and_then(|dealer| self.seats.player_after(dealer)) {
-                    DealerAction::TakeAction(
-                        ValidatedUserAction(UserAction{timestamp, username, action: Action::NextToDeal})
-                    )
+                    DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::NextToDeal }))
                 } else {
                     error!("Logic error: no dealer could be chosen: {:#?}", self);
                     DealerAction::Leave
                 }
             }
             State::Showdown => {
-                let winning_hands: Vec<_> = self.hands.iter()
+                let winning_hands: Vec<_> = self
+                    .hands
+                    .iter()
                     .map(|(&username, hand)| (username, hand.iter().chain(self.community.iter()).cloned().collect::<Vec<_>>()))
                     .filter_map(|(username, cards)| cards.try_into().ok().map(rank_7_card_hand).map(|hand| (username, hand)))
                     .max_items_by_key(|(_, hand)| *hand);
@@ -618,16 +560,14 @@ impl Game for TexasHoldEm {
                 info!("Showdown: all hands: {:?}", self.hands);
                 info!("Showdown: winning hands: {:?}", winning_hands);
                 if let Some(&(username, hand)) = winning_hands.first() {
-                    DealerAction::TakeAction(
-                        ValidatedUserAction(UserAction {
-                            timestamp,
-                            username,
-                            action: Action::WinHand {
-                                chips: (self.pot / winning_hands.len() as u64).min(self.max_winnings(username)),
-                                hand: Some(hand.to_string()),
-                            }
-                        })
-                    )
+                    DealerAction::TakeAction(ValidatedUserAction(UserAction {
+                        timestamp,
+                        username,
+                        action: Action::WinHand {
+                            chips: (self.pot / winning_hands.len() as u64).min(self.max_winnings(username)),
+                            hand: Some(hand.to_string()),
+                        },
+                    }))
                 } else {
                     error!("There were no winning hands in the showdown: {:#?}", self);
                     DealerAction::Leave
@@ -646,7 +586,7 @@ mod tests {
         let mut game = TexasHoldEm::new(0, settings, seed);
         for action in actions {
             match action.action {
-                Action::Join{..} | Action::Bet{..} | Action::Fold => {
+                Action::Join { .. } | Action::Bet { .. } | Action::Fold => {
                     let validated = game.validate_action(action.clone()).unwrap();
                     assert_eq!(ValidatedUserAction(action), validated);
                     game.take_action(validated).unwrap();
@@ -1080,7 +1020,8 @@ mod tests {
         ]"#;
         let actions = serde_json::from_str(actions).unwrap();
 
-        let settings = r#"{"format":"TexasHoldEm","title":"4-Player TexasHoldEm Test","max_players":4,"small_blind":25,"starting_stack":1000,"action_timeout":null}"#;
+        let settings =
+            r#"{"format":"TexasHoldEm","title":"4-Player TexasHoldEm Test","max_players":4,"small_blind":25,"starting_stack":1000,"action_timeout":null}"#;
         let settings = serde_json::from_str(settings).unwrap();
 
         let seed = r#"{"rng":"ChaCha20","seed":"48e2f45eb4a1ac6bc4ab4f2368ba2d9b0d7c1f132d7fc7f51036e92112dae136"}"#;
@@ -1088,7 +1029,7 @@ mod tests {
 
         test_game(actions, settings, seed);
     }
-    
+
     #[test]
     fn four_player_game_fold_triggers_next_card() {
         let actions = r#"[
@@ -1140,7 +1081,8 @@ mod tests {
         ]"#;
         let actions = serde_json::from_str(actions).unwrap();
 
-        let settings = r#"{"format":"TexasHoldEm","title":"4-Player TexasHoldEm Test","max_players":4,"small_blind":25,"starting_stack":1000,"action_timeout":null}"#;
+        let settings =
+            r#"{"format":"TexasHoldEm","title":"4-Player TexasHoldEm Test","max_players":4,"small_blind":25,"starting_stack":1000,"action_timeout":null}"#;
         let settings = serde_json::from_str(settings).unwrap();
 
         let seed = r#"{"rng":"ChaCha20","seed":"48e2f45eb4a1ac6bc4ab4f2368ba2d9b0d7c1f132d7fc7f51036e92112dae136"}"#;
@@ -1249,7 +1191,8 @@ mod tests {
         ]"#;
         let actions = serde_json::from_str(actions).unwrap();
 
-        let settings = r#"{"format":"TexasHoldEm","title":"4-Player TexasHoldEm Test","max_players":4,"small_blind":25,"starting_stack":1000,"action_timeout":null}"#;
+        let settings =
+            r#"{"format":"TexasHoldEm","title":"4-Player TexasHoldEm Test","max_players":4,"small_blind":25,"starting_stack":1000,"action_timeout":null}"#;
         let settings = serde_json::from_str(settings).unwrap();
 
         let seed = r#"{"rng":"ChaCha20","seed":"092b99f45313fff167029dc7420ed69a92becae492e09b65bc06ddcaae3c9e9c"}"#;
index fa5b9890af43f0687fb075133e5af7611467482f..dc7d7bc49c77146225485e26f459fd4616867c09 100644 (file)
@@ -74,30 +74,24 @@ impl KnockOutWhist {
     }
 
     fn hand_contains_card(&self, username: Username, card: Card) -> bool {
-        self.hands.get(&username)
-            .map_or(false, |hand| hand.contains(&card))
+        self.hands.get(&username).map_or(false, |hand| hand.contains(&card))
     }
 
     fn hand_contains_suit(&self, username: Username, suit: Suit) -> bool {
-        self.hands.get(&username)
-            .map_or(false, |hand| hand.iter().any(|card| card.suit == suit))
+        self.hands.get(&username).map_or(false, |hand| hand.iter().any(|card| card.suit == suit))
     }
 
     fn trick_winner(&self) -> Option<Username> {
-        if self.trick.len() < self.seats.players_len() { return None; }
-        let highest_trump = self.trick.iter()
-            .filter(|(_, card)| Some(card.suit) == self.trumps)
-            .max_by_key(|(_, card)| card.rank);
-        let highest_led = self.trick.iter()
-            .filter(|(_, card)| Some(card.suit) == self.led)
-            .max_by_key(|(_, card)| card.rank);
+        if self.trick.len() < self.seats.players_len() {
+            return None;
+        }
+        let highest_trump = self.trick.iter().filter(|(_, card)| Some(card.suit) == self.trumps).max_by_key(|(_, card)| card.rank);
+        let highest_led = self.trick.iter().filter(|(_, card)| Some(card.suit) == self.led).max_by_key(|(_, card)| card.rank);
         highest_trump.or(highest_led).map(|(username, _)| *username)
     }
 
-    fn cards_by_player(&self) -> impl Iterator<Item=(Username, Card)> + '_ {
-        self.hands.iter()
-            .map(|(username, hand)| hand.iter().map(move |card| (*username, *card)))
-            .flatten()
+    fn cards_by_player(&self) -> impl Iterator<Item = (Username, Card)> + '_ {
+        self.hands.iter().map(|(username, hand)| hand.iter().map(move |card| (*username, *card))).flatten()
     }
 }
 
@@ -114,12 +108,12 @@ impl Game for KnockOutWhist {
         self.actions_len
     }
 
-    fn validate_action(&self, UserAction{timestamp, username, action}: UserAction) -> Result<ValidatedUserAction, ActionError> {
+    fn validate_action(&self, UserAction { timestamp, username, action }: UserAction) -> Result<ValidatedUserAction, ActionError> {
         match (self.state, action) {
-            (_, Action::AddOn{..}) | (_, Action::RevealCard{..}) | (_, Action::Fold) | (_, Action::TimeoutFold) | (_, Action::Bet{..}) => {
+            (_, Action::AddOn { .. }) | (_, Action::RevealCard { .. }) | (_, Action::Fold) | (_, Action::TimeoutFold) | (_, Action::Bet { .. }) => {
                 Err(ActionError::InvalidActionForGameType)
             }
-            (State::NotStarted, Action::Join{seat, ..}) => {
+            (State::NotStarted, Action::Join { seat, .. }) => {
                 if self.seats.contains_player(username) {
                     Err(ActionError::AlreadyJoined)
                 } else if self.seats.players_len() > self.settings.max_players as usize {
@@ -127,27 +121,25 @@ impl Game for KnockOutWhist {
                 } else if !self.seats.seat_is_available(seat) {
                     Err(ActionError::SeatNotAvailable)
                 } else {
-                    Ok(ValidatedUserAction(UserAction{timestamp, username, action: Action::Join{seat, chips: 0}}))
+                    Ok(ValidatedUserAction(UserAction { timestamp, username, action: Action::Join { seat, chips: 0 } }))
                 }
             }
-            (State::Completed, Action::Join{..}) => Err(ActionError::GameHasEnded),
-            (_, Action::Join{..}) => Err(ActionError::GameHasStarted),
+            (State::Completed, Action::Join { .. }) => Err(ActionError::GameHasEnded),
+            (_, Action::Join { .. }) => Err(ActionError::GameHasStarted),
             (_, _) if !self.seats.contains_player(username) => Err(ActionError::NotAuthorised),
-            (State::NotStarted, Action::Leave) => {
-                Ok(ValidatedUserAction(UserAction{timestamp, username, action: Action::Leave}))
-            }
+            (State::NotStarted, Action::Leave) => Ok(ValidatedUserAction(UserAction { timestamp, username, action: Action::Leave })),
             (State::Completed, Action::Leave) => Err(ActionError::GameHasEnded),
             (_, Action::Leave) => Err(ActionError::GameHasStarted),
             (State::Dealing, _) => Err(ActionError::Dealing),
-            (State::ChoosingTrumps, Action::ChooseTrumps{suit}) => {
+            (State::ChoosingTrumps, Action::ChooseTrumps { suit }) => {
                 if Some(username) == self.call {
-                    Ok(ValidatedUserAction(UserAction{timestamp, username, action: Action::ChooseTrumps{suit}}))
+                    Ok(ValidatedUserAction(UserAction { timestamp, username, action: Action::ChooseTrumps { suit } }))
                 } else {
                     Err(ActionError::OutOfTurn)
                 }
             }
             (State::ChoosingTrumps, _) => Err(ActionError::MustChooseTrumps),
-            (State::Playing, Action::PlayCard{card}) => {
+            (State::Playing, Action::PlayCard { card }) => {
                 if Some(username) != self.active {
                     Err(ActionError::OutOfTurn)
                 } else if !self.hand_contains_card(username, card) {
@@ -155,7 +147,7 @@ impl Game for KnockOutWhist {
                 } else if matches!(self.led, Some(led) if card.suit != led && self.hand_contains_suit(username, led)) {
                     Err(ActionError::CardNotPlayable)
                 } else {
-                    Ok(ValidatedUserAction(UserAction{timestamp, username, action: Action::PlayCard{card}}))
+                    Ok(ValidatedUserAction(UserAction { timestamp, username, action: Action::PlayCard { card } }))
                 }
             }
             (State::CutForCall, _) => Err(ActionError::Dealing),
@@ -165,19 +157,13 @@ impl Game for KnockOutWhist {
         }
     }
 
-    fn take_action(&mut self, ValidatedUserAction(UserAction{username, action, ..}): ValidatedUserAction) -> Result<(), ActionError> {
+    fn take_action(&mut self, ValidatedUserAction(UserAction { username, action, .. }): ValidatedUserAction) -> Result<(), ActionError> {
         self.actions_len += 1;
         self.rng.advance();
         match (self.state, action) {
-            (_, Action::AddOn{..}) | (_, Action::Fold) | (_, Action::TimeoutFold) | (_, Action::Bet{..}) => {
-                Err(ActionError::InvalidActionForGameType)
-            }
-            (State::NotStarted, Action::Join{seat, ..}) => {
-                self.seats.add_player(seat, username)
-            }
-            (State::NotStarted, Action::Leave) => {
-                self.seats.remove_player(username)
-            }
+            (_, Action::AddOn { .. }) | (_, Action::Fold) | (_, Action::TimeoutFold) | (_, Action::Bet { .. }) => Err(ActionError::InvalidActionForGameType),
+            (State::NotStarted, Action::Join { seat, .. }) => self.seats.add_player(seat, username),
+            (State::NotStarted, Action::Leave) => self.seats.remove_player(username),
             (_, Action::NextToDeal) => {
                 self.dealer = Some(username);
                 self.deck = FIFTY_TWO_CARD_DECK.iter().cloned().collect();
@@ -190,7 +176,7 @@ impl Game for KnockOutWhist {
                 self.state = State::Dealing;
                 Ok(())
             }
-            (State::Dealing, Action::ReceiveCard{card: Some(card)}) => {
+            (State::Dealing, Action::ReceiveCard { card: Some(card) }) => {
                 self.deck.remove(&card);
                 self.hands.entry(username).or_default().insert(card);
                 if self.hands.values().all(|hand| hand.len() == self.cards_to_deal as usize) {
@@ -200,7 +186,7 @@ impl Game for KnockOutWhist {
                 }
                 Ok(())
             }
-            (State::Dealing, Action::CommunityCard{card}) => {
+            (State::Dealing, Action::CommunityCard { card }) => {
                 self.trump_card = Some(card);
                 self.trumps = Some(card.suit);
                 Ok(())
@@ -216,14 +202,14 @@ impl Game for KnockOutWhist {
                 }
                 Ok(())
             }
-            (State::ChoosingTrumps, Action::ChooseTrumps{suit}) => {
+            (State::ChoosingTrumps, Action::ChooseTrumps { suit }) => {
                 self.trumps = Some(suit);
                 self.call = None;
                 self.state = State::Playing;
                 self.active = self.dealer.and_then(|dealer| self.seats.player_after(dealer));
                 Ok(())
             }
-            (State::Playing, Action::PlayCard{card}) => {
+            (State::Playing, Action::PlayCard { card }) => {
                 if let Some(hand) = self.hands.get_mut(&username) {
                     hand.remove(&card);
                 }
@@ -260,16 +246,14 @@ impl Game for KnockOutWhist {
                 }
                 Ok(())
             }
-            (State::Playing, Action::KnockedOut) => {
-                self.seats.remove_player(username)
-            }
+            (State::Playing, Action::KnockedOut) => self.seats.remove_player(username),
             (State::Playing, Action::WinGame) => {
                 self.winners.clear();
                 self.winners.insert(username);
                 self.state = State::Completed;
                 Ok(())
             }
-            (State::CutForCall, Action::RevealCard{card}) => {
+            (State::CutForCall, Action::RevealCard { card }) => {
                 self.deck.remove(&card);
                 self.hands.entry(username).or_default().insert(card);
                 if self.hands.values().map(HashSet::len).sum::<usize>() == self.winners.len() {
@@ -306,11 +290,10 @@ impl Game for KnockOutWhist {
         let mut rng = self.rng.clone();
         match self.state {
             State::NotStarted => {
-                if self.seats.players_len() == self.settings.max_players as usize { // TODO
+                if self.seats.players_len() == self.settings.max_players as usize {
+                    // TODO
                     if let Some(username) = rng.choose_from(self.seats.player_set()) {
-                        return DealerAction::TakeAction(
-                            ValidatedUserAction(UserAction{timestamp, username, action: Action::NextToDeal})
-                        );
+                        return DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::NextToDeal }));
                     }
                 }
                 DealerAction::WaitForPlayer
@@ -318,59 +301,45 @@ impl Game for KnockOutWhist {
             State::Dealing => {
                 if let Some(username) = self.receiver {
                     let card = rng.choose_from(&self.deck).cloned();
-                    DealerAction::TakeAction(
-                        ValidatedUserAction(UserAction{timestamp, username, action: Action::ReceiveCard{card}})
-                    )
+                    DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::ReceiveCard { card } }))
                 } else if let Some(username) = self.dealer {
                     match (self.call, self.trump_card) {
                         (None, None) => {
                             if let Some(&card) = rng.choose_from(&self.deck) {
-                                DealerAction::TakeAction(
-                                    ValidatedUserAction(UserAction{timestamp, username, action: Action::CommunityCard{card}})
-                                )
+                                DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::CommunityCard { card } }))
                             } else {
                                 error!("Expected to deal a card but none were left in deck");
                                 DealerAction::Leave
                             }
                         }
-                        (Some(_), _) | (None, Some(_)) => DealerAction::TakeAction(
-                            ValidatedUserAction(UserAction{timestamp, username, action: Action::EndDeal})
-                        ),
+                        (Some(_), _) | (None, Some(_)) => {
+                            DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::EndDeal }))
+                        }
                     }
                 } else {
                     error!("Expected to deal a card but there was no dealer");
                     DealerAction::Leave
                 }
             }
-            State::ChoosingTrumps => {
-                DealerAction::WaitForPlayer
-            }
+            State::ChoosingTrumps => DealerAction::WaitForPlayer,
             State::Playing => {
                 if !self.winners.is_empty() {
                     for username in self.seats.player_set() {
                         if matches!(self.tricks_won.get(&username), Some(0) | None) {
-                            return DealerAction::TakeAction(
-                                ValidatedUserAction(UserAction{timestamp, username, action: Action::KnockedOut})
-                            );
+                            return DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::KnockedOut }));
                         }
                     }
                     if self.seats.players_len() == 1 {
                         if let Some(&username) = self.winners.iter().next() {
-                            return DealerAction::TakeAction(
-                                ValidatedUserAction(UserAction{timestamp, username, action: Action::WinGame})
-                            );
+                            return DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::WinGame }));
                         }
                     }
                     if let Some(username) = self.call {
-                        return DealerAction::TakeAction(
-                            ValidatedUserAction(UserAction{timestamp, username, action: Action::WinCall})
-                        );
+                        return DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::WinCall }));
                     }
                     DealerAction::WaitForPlayer
                 } else if let Some(username) = self.trick_winner() {
-                    DealerAction::TakeAction(
-                        ValidatedUserAction(UserAction{timestamp, username, action: Action::WinTrick})
-                    )
+                    DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::WinTrick }))
                 } else {
                     DealerAction::WaitForPlayer
                 }
@@ -378,17 +347,13 @@ impl Game for KnockOutWhist {
             State::CutForCall => {
                 if let Some(username) = self.receiver {
                     if let Some(card) = rng.choose_from(&self.deck).cloned() {
-                        DealerAction::TakeAction(
-                            ValidatedUserAction(UserAction{timestamp, username, action: Action::RevealCard{card}})
-                        )
+                        DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::RevealCard { card } }))
                     } else {
                         error!("Expected to cut for call but there were no cards left in the deck");
                         DealerAction::Leave
                     }
                 } else if let Some(username) = self.call {
-                    DealerAction::TakeAction(
-                        ValidatedUserAction(UserAction{timestamp, username, action: Action::WinCall})
-                    )
+                    DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::WinCall }))
                 } else {
                     error!("Cutting for call but there is nobody to receive a card");
                     DealerAction::Leave
@@ -396,17 +361,13 @@ impl Game for KnockOutWhist {
             }
             State::RoundCompleted => {
                 if let Some(username) = self.dealer.and_then(|dealer| self.seats.player_after(dealer)) {
-                    DealerAction::TakeAction(
-                        ValidatedUserAction(UserAction{timestamp, username, action: Action::NextToDeal})
-                    )
+                    DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::NextToDeal }))
                 } else {
                     error!("Round completed but there is nobody to deal");
                     DealerAction::Leave
                 }
             }
-            State::Completed => {
-                DealerAction::Leave
-            }
+            State::Completed => DealerAction::Leave,
         }
     }
 }
@@ -419,7 +380,7 @@ mod tests {
         let mut game = KnockOutWhist::new(0, settings, seed);
         for action in actions {
             match action.action {
-                Action::Join{..} | Action::PlayCard{..} | Action::ChooseTrumps{..} => {
+                Action::Join { .. } | Action::PlayCard { .. } | Action::ChooseTrumps { .. } => {
                     let validated = game.validate_action(action.clone()).unwrap();
                     assert_eq!(ValidatedUserAction(action), validated);
                     game.take_action(validated).unwrap();
index 9f3e20aa84a17b2e57cf5b4d4b674d916cc7bd2e..b1dd49a7ef82d53fda796ed990deb82ffd0e0ec8 100644 (file)
@@ -1,17 +1,25 @@
-#[macro_use] extern crate log;
-#[macro_use] extern crate serde_derive;
+#[macro_use]
+extern crate log;
+#[macro_use]
+extern crate serde_derive;
 
 use std::collections::HashSet;
 use std::convert::TryFrom;
 use std::mem::swap;
 
 use async_std::task::spawn;
-use futures::{channel::mpsc::{channel, Receiver}, future::{Either, select}, select, FutureExt, SinkExt, StreamExt, pin_mut, stream::{self, FuturesUnordered}};
+use futures::{
+    channel::mpsc::{channel, Receiver},
+    future::{select, Either},
+    pin_mut, select,
+    stream::{self, FuturesUnordered},
+    FutureExt, SinkExt, StreamExt,
+};
 use redis::{aio::PubSub, Client, Msg};
 use signal_hook::consts::signal::*;
 use signal_hook_async_std::Signals;
-use tide::{Body, Request, Error, Response, StatusCode};
 use tide::utils::After;
+use tide::{Body, Error, Request, Response, StatusCode};
 //use tide_rustls::TlsListener;
 use tide_websockets::{Message, WebSocket, WebSocketConnection};
 
@@ -56,7 +64,7 @@ pub async fn handle_client_interest(mut connection: PubSub, mut new_clients: Rec
             let mut next_interest = connection_on_message.next().fuse();
             let mut next_client = new_clients.next().fuse();
             let mut next_client_interest = FuturesUnordered::new();
-            for (index, Client{ref mut sender, ..}) in clients.iter_mut().enumerate() {
+            for (index, Client { ref mut sender, .. }) in clients.iter_mut().enumerate() {
                 next_client_interest.push(sender.register_interest.next().map(move |interest| (index, interest)));
             }
             let mut next_client_interest = next_client_interest.select_next_some();
@@ -85,15 +93,15 @@ pub async fn handle_client_interest(mut connection: PubSub, mut new_clients: Rec
             }
             action
         } {
-            Action::AddClient{sender} => {
+            Action::AddClient { sender } => {
                 debug!("handle_client_interest: Action::AddClient {{ clients[{}] }}", clients.len());
-                clients.push(Client{sender, interests: HashSet::new()});
+                clients.push(Client { sender, interests: HashSet::new() });
             }
-            Action::RegisterInterest{index, mut client_interests} => {
+            Action::RegisterInterest { index, mut client_interests } => {
                 debug!("handle_client_interest: Action::RegisterInterest{{ client_interests: {:?}, index: {:?} }}", client_interests, index);
-                let interests_prior: HashSet<ClientInterest> = clients.iter().map(|Client{ref interests, ..}| interests.clone()).flatten().collect();
+                let interests_prior: HashSet<ClientInterest> = clients.iter().map(|Client { ref interests, .. }| interests.clone()).flatten().collect();
                 swap(&mut clients[index].interests, &mut client_interests);
-                let interests_after: HashSet<ClientInterest> = clients.iter().map(|Client{ref interests, ..}| interests.clone()).flatten().collect();
+                let interests_after: HashSet<ClientInterest> = clients.iter().map(|Client { ref interests, .. }| interests.clone()).flatten().collect();
                 for interest in &interests_after - &interests_prior {
                     debug!("handle_client_interest: Subscribing to {:?}", interest);
                     if let Err(err) = connection.subscribe(interest).await {
@@ -115,11 +123,11 @@ pub async fn handle_client_interest(mut connection: PubSub, mut new_clients: Rec
                     }
                 }
             }
-            Action::SendInterest{interest} => {
+            Action::SendInterest { interest } => {
                 debug!("handle_client_interest: Action::SendInterest {{ interest: {:?} }}", interest);
                 match TryFrom::try_from(interest) {
                     Ok(interest) => {
-                        for (index, Client{sender, interests}) in clients.iter_mut().enumerate() {
+                        for (index, Client { sender, interests }) in clients.iter_mut().enumerate() {
                             if interests.contains(&interest) {
                                 debug!("handle_client_interest: Sending {:?} to clients[{}]", interest, index);
                                 if let Err(err) = sender.interest.send(interest.clone()).await {
@@ -127,18 +135,18 @@ pub async fn handle_client_interest(mut connection: PubSub, mut new_clients: Rec
                                 }
                             }
                         }
-                    },
-                    Err(ClientInterestFromMsgError::TimeoutMessageWasNotExpired) => {},
-                    Err(ClientInterestFromMsgError::InvalidChannelName{channel_name}) => {
+                    }
+                    Err(ClientInterestFromMsgError::TimeoutMessageWasNotExpired) => {}
+                    Err(ClientInterestFromMsgError::InvalidChannelName { channel_name }) => {
                         error!("handle_client_interest: Failed to interest {} to ClientInterest", channel_name);
                     }
                 }
             }
-            Action::RemoveClient{index} => {
+            Action::RemoveClient { index } => {
                 debug!("handle_client_interest: Action::RemoveClient {{ index: {:?} }}", index);
-                let interests_prior: HashSet<ClientInterest> = clients.iter().map(|Client{ref interests, ..}| interests.clone()).flatten().collect();
+                let interests_prior: HashSet<ClientInterest> = clients.iter().map(|Client { ref interests, .. }| interests.clone()).flatten().collect();
                 clients.remove(index);
-                let interests_after: HashSet<ClientInterest> = clients.iter().map(|Client{ref interests, ..}| interests.clone()).flatten().collect();
+                let interests_after: HashSet<ClientInterest> = clients.iter().map(|Client { ref interests, .. }| interests.clone()).flatten().collect();
                 for interest in &interests_prior - &interests_after {
                     debug!("handle_client_interest: Unsubscribing from {:?}", interest);
                     if let Err(err) = connection.unsubscribe(interest).await {
@@ -186,7 +194,9 @@ async fn handle_new_games(server: Server) -> Result<(), std::io::Error> {
                 return Err(std::io::Error::new(std::io::ErrorKind::Other, err));
             }
         }
-        if let Some(ClientInterest::GameList) = update_stream.next().await { continue; }
+        if let Some(ClientInterest::GameList) = update_stream.next().await {
+            continue;
+        }
         return Ok(());
     }
 }
@@ -202,7 +212,7 @@ pub async fn handle_websocket_request(request: Request<Server>, stream: WebSocke
             Either::Left(Ok(Message::Text(input))) => {
                 let response = match serde_json::from_str(&input) {
                     Ok(message) => client.apply_message(message).await,
-                    Err(err) => ServerMessage::ProtocolError{reason: err.to_string()},
+                    Err(err) => ServerMessage::ProtocolError { reason: err.to_string() },
                 };
                 stream.send_json(&response).await?;
             }
@@ -241,12 +251,8 @@ async fn handle_signals(mut signals: Signals) -> Result<(), std::io::Error> {
 
 async fn serve_404(response: Response) -> Result<Response, Error> {
     match response.status() {
-        StatusCode::NotFound =>
-            Ok(Response::builder(404)
-                .body(Body::from_file("site/404.html").await?)
-                .content_type("text/html")
-                .build()),
-        _ => Ok(response)
+        StatusCode::NotFound => Ok(Response::builder(404).body(Body::from_file("site/404.html").await?).content_type("text/html").build()),
+        _ => Ok(response),
     }
 }
 
@@ -282,7 +288,10 @@ async fn main() -> Result<(), Error> {
     let app = app.listen("0.0.0.0:8080");
     pin_mut!(app, handle_client_interest, handle_new_games, signal_handler);
 
-    select(select(app, select(handle_client_interest, handle_new_games).map(|f| f.factor_first().0)).map(|f| f.factor_first().0), signal_handler).await.factor_first().0?;
+    select(select(app, select(handle_client_interest, handle_new_games).map(|f| f.factor_first().0)).map(|f| f.factor_first().0), signal_handler)
+        .await
+        .factor_first()
+        .0?;
 
     info!("Pokerwave shut down gracefully.");
 
index 45f7ecc19e8ee507de11bdfbfe6e894e73e876a4..5d2922778242f74256707e3b4708fc6247cd4b39 100644 (file)
@@ -1,8 +1,8 @@
-use std::cmp::{Ord, Eq};
+use std::cmp::{Eq, Ord};
 
 use getrandom::getrandom;
 use itertools::Itertools;
-use rand::{SeedableRng, CryptoRng, Rng, RngCore, Error, seq::IteratorRandom};
+use rand::{seq::IteratorRandom, CryptoRng, Error, Rng, RngCore, SeedableRng};
 use rand_chacha::ChaCha20Rng;
 
 #[derive(Copy, Clone, Debug, Serialize, Deserialize)]
@@ -10,7 +10,7 @@ use rand_chacha::ChaCha20Rng;
 pub enum Seed {
     ChaCha20 {
         #[serde(with = "hex")]
-        seed: [u8; 32]
+        seed: [u8; 32],
     },
 }
 
@@ -18,14 +18,12 @@ impl Seed {
     pub fn cha_cha_20_from_entropy() -> Self {
         let mut seed = [0u8; 32];
         getrandom(&mut seed).expect("getrandom failed when generating random seed");
-        Self::ChaCha20{seed}
+        Self::ChaCha20 { seed }
     }
 
     pub fn into_rng(self) -> WaveRng {
         match self {
-            Seed::ChaCha20{seed} => {
-                WaveRng::ChaCha20(ChaCha20Rng::from_seed(seed))
-            }
+            Seed::ChaCha20 { seed } => WaveRng::ChaCha20(ChaCha20Rng::from_seed(seed)),
         }
     }
 }
@@ -42,7 +40,7 @@ impl WaveRng {
         };
     }
 
-    pub fn choose_from<T: Eq + Ord, I: IntoIterator<Item=T>>(&mut self, elements: I) -> Option<T> {
+    pub fn choose_from<T: Eq + Ord, I: IntoIterator<Item = T>>(&mut self, elements: I) -> Option<T> {
         elements.into_iter().sorted().choose_stable(self)
     }
 }
@@ -92,9 +90,8 @@ mod tests {
             *i = rng.next_u32();
         }
         let expected = [
-            0xade0b876, 0x903df1a0, 0xe56a5d40, 0x28bd8653, 0xb819d2bd, 0x1aed8da0, 0xccef36a8,
-            0xc70d778b, 0x7c5941da, 0x8d485751, 0x3fe02477, 0x374ad8b8, 0xf4b8436a, 0x1ca11815,
-            0x69b687c3, 0x8665eeb2,
+            0xade0b876, 0x903df1a0, 0xe56a5d40, 0x28bd8653, 0xb819d2bd, 0x1aed8da0, 0xccef36a8, 0xc70d778b, 0x7c5941da, 0x8d485751, 0x3fe02477, 0x374ad8b8,
+            0xf4b8436a, 0x1ca11815, 0x69b687c3, 0x8665eeb2,
         ];
         assert_eq!(results, expected);
 
@@ -102,9 +99,8 @@ mod tests {
             *i = rng.next_u32();
         }
         let expected = [
-            0xbee7079f, 0x7a385155, 0x7c97ba98, 0x0d082d73, 0xa0290fcb, 0x6965e348, 0x3e53c612,
-            0xed7aee32, 0x7621b729, 0x434ee69c, 0xb03371d5, 0xd539d874, 0x281fed31, 0x45fb0a51,
-            0x1f0ae1ac, 0x6f4d794b,
+            0xbee7079f, 0x7a385155, 0x7c97ba98, 0x0d082d73, 0xa0290fcb, 0x6965e348, 0x3e53c612, 0xed7aee32, 0x7621b729, 0x434ee69c, 0xb03371d5, 0xd539d874,
+            0x281fed31, 0x45fb0a51, 0x1f0ae1ac, 0x6f4d794b,
         ];
         assert_eq!(results, expected);
     }
index 7bc7fdc0617a527b887b7d8335b0267bc7bb9c15..66b489d4c1ad466e649337719ea3ddd3aa60f6a8 100644 (file)
@@ -10,15 +10,13 @@ pub struct Seats {
 
 impl Seats {
     pub fn new() -> Self {
-        Self {
-            players: BTreeMap::new(),
-        }
+        Self { players: BTreeMap::new() }
     }
 
     pub fn add_player(&mut self, seat: u32, username: Username) -> Result<(), ActionError> {
         match self.players.insert(seat, username) {
             Some(_) => Err(ActionError::SeatNotAvailable),
-            None => Ok(())
+            None => Ok(()),
         }
     }
 
@@ -34,7 +32,7 @@ impl Seats {
     }
 
     fn player_after_seat(&self, seat: u32) -> Option<Username> {
-        if let Some((_, &name)) = self.players.range(seat+1..).next() {
+        if let Some((_, &name)) = self.players.range(seat + 1..).next() {
             Some(name)
         } else if let Some((_, &name)) = self.players.range(..seat).next() {
             Some(name)
@@ -46,7 +44,7 @@ impl Seats {
     pub fn player_after_where(&self, username: Username, condition: impl Fn(Username) -> bool) -> Option<Username> {
         for (&seat, &player) in &self.players {
             if player == username {
-                for (_, &name) in self.players.range(seat+1..) {
+                for (_, &name) in self.players.range(seat + 1..) {
                     if condition(name) {
                         return Some(name);
                     }
index 3f804c7e4dbd240fc70c61eadbeb38e8a117bdd1..a45956ab05c711fe094d56f0cbb8f1b5db052bb0 100644 (file)
@@ -1,9 +1,12 @@
 use std::collections::HashSet;
 use std::convert::TryFrom;
 
-use futures::{channel::mpsc::{Receiver, Sender, channel}, SinkExt};
-use redis::{AsyncCommands, ErrorKind, FromRedisValue, Msg, RedisError, RedisResult, RedisWrite, Script, ToRedisArgs, Value, aio::MultiplexedConnection, cmd};
-use serde::{Serialize, Deserialize};
+use futures::{
+    channel::mpsc::{channel, Receiver, Sender},
+    SinkExt,
+};
+use redis::{aio::MultiplexedConnection, cmd, AsyncCommands, ErrorKind, FromRedisValue, Msg, RedisError, RedisResult, RedisWrite, Script, ToRedisArgs, Value};
+use serde::{Deserialize, Serialize};
 
 use crate::auth::Auth;
 use crate::client::ClientInterest;
@@ -35,14 +38,14 @@ fn client_interest_channel() -> (ClientInterestSender, ClientInterestReceiver) {
     const INTEREST_CHANNEL_BUFFER: usize = 1024;
     let (register_interest_tx, register_interest_rx) = channel(REGISTER_INTEREST_CHANNEL_BUFFER);
     let (interest_tx, interest_rx) = channel(INTEREST_CHANNEL_BUFFER);
-    let tx = ClientInterestSender{register_interest: register_interest_rx, interest: interest_tx};
-    let rx = ClientInterestReceiver{register_interest: register_interest_tx, interest: interest_rx};
+    let tx = ClientInterestSender { register_interest: register_interest_rx, interest: interest_tx };
+    let rx = ClientInterestReceiver { register_interest: register_interest_tx, interest: interest_rx };
     (tx, rx)
 }
 
 #[derive(Debug)]
 pub enum ClientInterestFromMsgError {
-    InvalidChannelName{channel_name: String},
+    InvalidChannelName { channel_name: String },
     TimeoutMessageWasNotExpired,
 }
 
@@ -54,41 +57,42 @@ impl TryFrom<Msg> for ClientInterest {
             Ok(ClientInterest::GameList)
         } else if let Some(username) = channel_name.strip_prefix("__keyspace@0__:user:") {
             match username.parse() {
-                Ok(username) => Ok(ClientInterest::User{username}),
-                Err(_) => Err(ClientInterestFromMsgError::InvalidChannelName{channel_name: channel_name.to_string()}),
+                Ok(username) => Ok(ClientInterest::User { username }),
+                Err(_) => Err(ClientInterestFromMsgError::InvalidChannelName { channel_name: channel_name.to_string() }),
             }
         } else if let Some(key) = channel_name.strip_prefix("__keyspace@0__:game:") {
             if let Some(id) = key.strip_suffix(":actions") {
                 match id.parse() {
-                    Ok(id) => Ok(ClientInterest::Game{id}),
-                    Err(_) => Err(ClientInterestFromMsgError::InvalidChannelName{channel_name: channel_name.to_string()}),
+                    Ok(id) => Ok(ClientInterest::Game { id }),
+                    Err(_) => Err(ClientInterestFromMsgError::InvalidChannelName { channel_name: channel_name.to_string() }),
                 }
             } else if let Some(id) = key.strip_suffix(":timeout") {
                 match msg.get_payload::<String>() {
                     Ok(str) if str == "expired" => match id.parse() {
-                        Ok(id) => Ok(ClientInterest::Timeout{id}),
-                        Err(_) => Err(ClientInterestFromMsgError::InvalidChannelName{channel_name: channel_name.to_string()}),
+                        Ok(id) => Ok(ClientInterest::Timeout { id }),
+                        Err(_) => Err(ClientInterestFromMsgError::InvalidChannelName { channel_name: channel_name.to_string() }),
                     },
                     _ => Err(ClientInterestFromMsgError::TimeoutMessageWasNotExpired),
                 }
             } else {
-                Err(ClientInterestFromMsgError::InvalidChannelName{channel_name: channel_name.to_string()})
+                Err(ClientInterestFromMsgError::InvalidChannelName { channel_name: channel_name.to_string() })
             }
         } else {
-            Err(ClientInterestFromMsgError::InvalidChannelName{channel_name: channel_name.to_string()})
+            Err(ClientInterestFromMsgError::InvalidChannelName { channel_name: channel_name.to_string() })
         }
     }
 }
 
 impl ToRedisArgs for ClientInterest {
     fn write_redis_args<W>(&self, out: &mut W)
-        where W: ?Sized + RedisWrite
+    where
+        W: ?Sized + RedisWrite,
     {
         match self {
             ClientInterest::GameList => out.write_arg(b"__keyspace@0__:game:list"),
-            ClientInterest::Game{id} => out.write_arg_fmt(format!("__keyspace@0__:game:{}:actions", id)),
-            ClientInterest::User{username} => out.write_arg_fmt(format!("__keyspace@0__:user:{}", username)),
-            ClientInterest::Timeout{id} => out.write_arg_fmt(format!("__keyspace@0__:game:{}:timeout", id)),
+            ClientInterest::Game { id } => out.write_arg_fmt(format!("__keyspace@0__:game:{}:actions", id)),
+            ClientInterest::User { username } => out.write_arg_fmt(format!("__keyspace@0__:user:{}", username)),
+            ClientInterest::Timeout { id } => out.write_arg_fmt(format!("__keyspace@0__:game:{}:timeout", id)),
         }
     }
 }
@@ -108,19 +112,16 @@ const TAKE_ACTION_LUA_SCRIPT: &'static str = r#"
 
 impl Server {
     pub fn new(redis: MultiplexedConnection, register_update_stream: Sender<ClientInterestSender>) -> Self {
-        Self {
-            redis,
-            register_update_stream
-        }
+        Self { redis, register_update_stream }
     }
 
     pub async fn new_state(&self) -> (ServerState, Receiver<ClientInterest>) {
-        let (client_interest_sender, ClientInterestReceiver{register_interest, interest}) = client_interest_channel();
+        let (client_interest_sender, ClientInterestReceiver { register_interest, interest }) = client_interest_channel();
         if let Err(err) = self.register_update_stream.clone().send(client_interest_sender).await {
             error!("new_state: Send failed: {}", err);
         }
         let take_action_script = Script::new(TAKE_ACTION_LUA_SCRIPT);
-        (ServerState{redis: self.redis.clone(), register_interest, take_action_script}, interest)
+        (ServerState { redis: self.redis.clone(), register_interest, take_action_script }, interest)
     }
 }
 
@@ -253,7 +254,7 @@ impl FromRedisValue for ActionStatus {
         match value {
             Value::Int(0) => Ok(ActionStatus::Interrupted),
             Value::Int(_) => Ok(ActionStatus::Committed),
-            _ => Err(RedisError::from((ErrorKind::TypeError, "ActionStatus may only be converted from an integer")))
+            _ => Err(RedisError::from((ErrorKind::TypeError, "ActionStatus may only be converted from an integer"))),
         }
     }
 }
@@ -268,29 +269,27 @@ impl<T> AsJson<T> {
 }
 
 impl<T> FromRedisValue for AsJson<T>
-    where T: for<'a> Deserialize<'a>
+where
+    T: for<'a> Deserialize<'a>,
 {
     fn from_redis_value(value: &Value) -> RedisResult<Self> {
         match value {
-            Value::Data(ref bytes) => serde_json::from_slice(bytes).map(AsJson)
-                .map_err(|err| {
-                    error!("Failed to parse JSON from redis: {}", err);
-                    RedisError::from((
-                        ErrorKind::TypeError,
-                        "Failed to parse as JSON",
-                        err.to_string()
-                    ))
-                }),
+            Value::Data(ref bytes) => serde_json::from_slice(bytes).map(AsJson).map_err(|err| {
+                error!("Failed to parse JSON from redis: {}", err);
+                RedisError::from((ErrorKind::TypeError, "Failed to parse as JSON", err.to_string()))
+            }),
             _ => Err(RedisError::from((ErrorKind::TypeError, "Value was not Value::Data"))),
         }
     }
 }
 
 impl<T> ToRedisArgs for AsJson<T>
-    where T: Serialize
+where
+    T: Serialize,
 {
     fn write_redis_args<W>(&self, out: &mut W)
-        where W: ?Sized + RedisWrite
+    where
+        W: ?Sized + RedisWrite,
     {
         match serde_json::to_vec(&self.0) {
             Ok(bytes) => out.write_arg(&bytes),
@@ -311,50 +310,32 @@ mod tests {
 
     #[test]
     fn convert_channel_names_to_interest() {
-        let msg = Value::Bulk(vec![
-            Value::Data(b"message".to_vec()),
-            Value::Data(b"__keyspace@0__:game:list".to_vec()),
-            Value::Data(b"expire".to_vec()),
-        ]);
+        let msg = Value::Bulk(vec![Value::Data(b"message".to_vec()), Value::Data(b"__keyspace@0__:game:list".to_vec()), Value::Data(b"expire".to_vec())]);
         let msg = Msg::from_value(&msg).unwrap();
         let interest = ClientInterest::try_from(msg).unwrap();
         assert_eq!(ClientInterest::GameList, interest);
 
-        let msg = Value::Bulk(vec![
-            Value::Data(b"message".to_vec()),
-            Value::Data(b"__keyspace@0__:user:player1".to_vec()),
-            Value::Data(b"set".to_vec()),
-        ]);
+        let msg = Value::Bulk(vec![Value::Data(b"message".to_vec()), Value::Data(b"__keyspace@0__:user:player1".to_vec()), Value::Data(b"set".to_vec())]);
         let msg = Msg::from_value(&msg).unwrap();
         let interest = ClientInterest::try_from(msg).unwrap();
-        assert_eq!(ClientInterest::User{username: "player1".parse().unwrap()}, interest);
+        assert_eq!(ClientInterest::User { username: "player1".parse().unwrap() }, interest);
 
-        let msg = Value::Bulk(vec![
-            Value::Data(b"message".to_vec()),
-            Value::Data(b"__keyspace@0__:game:12345:actions".to_vec()),
-            Value::Data(b"rpush".to_vec()),
-        ]);
+        let msg =
+            Value::Bulk(vec![Value::Data(b"message".to_vec()), Value::Data(b"__keyspace@0__:game:12345:actions".to_vec()), Value::Data(b"rpush".to_vec())]);
         let msg = Msg::from_value(&msg).unwrap();
         let interest = ClientInterest::try_from(msg).unwrap();
-        assert_eq!(ClientInterest::Game{id: 12345}, interest);
+        assert_eq!(ClientInterest::Game { id: 12345 }, interest);
 
-        let msg = Value::Bulk(vec![
-            Value::Data(b"message".to_vec()),
-            Value::Data(b"__keyspace@0__:game:12345:timeout".to_vec()),
-            Value::Data(b"expired".to_vec()),
-        ]);
+        let msg =
+            Value::Bulk(vec![Value::Data(b"message".to_vec()), Value::Data(b"__keyspace@0__:game:12345:timeout".to_vec()), Value::Data(b"expired".to_vec())]);
         let msg = Msg::from_value(&msg).unwrap();
         let interest = ClientInterest::try_from(msg).unwrap();
-        assert_eq!(ClientInterest::Timeout{id: 12345}, interest);
+        assert_eq!(ClientInterest::Timeout { id: 12345 }, interest);
     }
 
     #[test]
     fn ignores_timeout_key_set() {
-        let msg = Value::Bulk(vec![
-            Value::Data(b"message".to_vec()),
-            Value::Data(b"__keyspace@0__:game:12345:timeout".to_vec()),
-            Value::Data(b"set".to_vec()),
-        ]);
+        let msg = Value::Bulk(vec![Value::Data(b"message".to_vec()), Value::Data(b"__keyspace@0__:game:12345:timeout".to_vec()), Value::Data(b"set".to_vec())]);
         let msg = Msg::from_value(&msg).unwrap();
         assert!(ClientInterest::try_from(msg).is_err());
     }
index cdf9d9cc25cd92cd3661d4e1006513a0c23ae69c..b505c74685529ac1af775600d9b5f2dcfefa69dc 100644 (file)
@@ -1,7 +1,11 @@
 use std::fmt::{self, Debug, Display, Formatter};
 use std::str::{self, FromStr, Utf8Error};
 
-use serde::{Serialize, Deserialize, Serializer, Deserializer, ser::Error, de::{self, Visitor}};
+use serde::{
+    de::{self, Visitor},
+    ser::Error,
+    Deserialize, Deserializer, Serialize, Serializer,
+};
 
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[repr(align(8))]
@@ -14,19 +18,12 @@ impl Username {
     const MAX_LENGTH: usize = 32;
 
     fn as_str(&self) -> Result<&str, Utf8Error> {
-        str::from_utf8(&self.username)
-            .map(|str| str.trim_end_matches('\0'))
+        str::from_utf8(&self.username).map(|str| str.trim_end_matches('\0'))
     }
 }
 
-pub const DEALER: Username = Username {
-    username: [
-        b'd', b'e', b'a', b'l', b'e', b'r', 0, 0,
-        0, 0, 0, 0, 0, 0, 0, 0,
-        0, 0, 0, 0, 0, 0, 0, 0,
-        0, 0, 0, 0, 0, 0, 0, 0,
-    ],
-};
+pub const DEALER: Username =
+    Username { username: [b'd', b'e', b'a', b'l', b'e', b'r', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] };
 
 impl Display for Username {
     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
@@ -61,14 +58,15 @@ impl FromStr for Username {
         } else {
             let mut username = [0; 32];
             username[0..len].copy_from_slice(bytes);
-            Ok(Username{username})
+            Ok(Username { username })
         }
     }
 }
 
 impl Serialize for Username {
     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-        where S: Serializer
+    where
+        S: Serializer,
     {
         match self.as_str() {
             Ok(str) => serializer.serialize_str(str),
@@ -79,7 +77,8 @@ impl Serialize for Username {
 
 impl<'de> Deserialize<'de> for Username {
     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-        where D: Deserializer<'de>
+    where
+        D: Deserializer<'de>,
     {
         struct StrVisitor;
         impl Visitor<'_> for StrVisitor {
@@ -89,7 +88,8 @@ impl<'de> Deserialize<'de> for Username {
             }
 
             fn visit_str<E>(self, str: &str) -> Result<Username, E>
-                where E: de::Error
+            where
+                E: de::Error,
             {
                 str.parse().map_err(E::custom)
             }
@@ -105,7 +105,10 @@ mod tests {
     #[test]
     fn deserialize_username() {
         let username: Username = serde_json::from_str(r#""user1234""#).unwrap();
-        assert_eq!(username, Username{username: [b'u', b's', b'e', b'r', b'1', b'2', b'3', b'4', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]});
+        assert_eq!(
+            username,
+            Username { username: [b'u', b's', b'e', b'r', b'1', b'2', b'3', b'4', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }
+        );
         assert!(serde_json::from_str::<Username>(r#""\0""#).is_err());
         assert!(serde_json::from_str::<Username>(r#""this_is_a_very_long_username_that_fails_to_parse""#).is_err());
     }
index e68d5ce3645f9b8d7d0acff4ab5da41ca4ab46b5..056bfe132cc60569ae12766194acb4abbbe4b27b 100644 (file)
@@ -14,17 +14,14 @@ pub struct DedupReady<S: Stream> {
     item: Option<S::Item>,
 }
 
-impl <S: Stream> DedupReady<S> {
+impl<S: Stream> DedupReady<S> {
     fn new(stream: S) -> Self {
-        Self {
-            stream: stream.fuse(),
-            item: None,
-        }
+        Self { stream: stream.fuse(), item: None }
     }
 }
 
 impl<S: Stream> Stream for DedupReady<S>
-where 
+where
     S::Item: PartialEq,
 {
     type Item = S::Item;
@@ -40,12 +37,12 @@ where
                         return Poll::Ready(Some(next));
                     }
                     Poll::Ready(None) | Poll::Pending => return Poll::Ready(this.item.take()),
-                }
+                },
                 None => match this.stream.as_mut().poll_next(cx) {
                     Poll::Ready(Some(item)) => *this.item = Some(item),
                     Poll::Ready(None) => return Poll::Ready(None),
                     Poll::Pending => return Poll::Pending,
-                }
+                },
             }
         }
     }
@@ -60,7 +57,7 @@ where
     }
 }
 
-pub trait DedupReadyExt : Stream {
+pub trait DedupReadyExt: Stream {
     fn dedup_ready(self) -> DedupReady<Self>
     where
         Self::Item: PartialEq,
@@ -76,7 +73,12 @@ impl<S: Stream> DedupReadyExt for S {}
 mod tests {
     use super::*;
 
-    use futures::{channel::mpsc::channel, pin_mut, stream::{empty, once, iter}, SinkExt};
+    use futures::{
+        channel::mpsc::channel,
+        pin_mut,
+        stream::{empty, iter, once},
+        SinkExt,
+    };
 
     #[async_std::test]
     async fn empty_stream() {
index b7ef75e47b4646478856d4e97e8b53b9049ddf2e..5dd6469484eaf019ff37e70bb7e6c86e8114e7d2 100644 (file)
@@ -2,7 +2,7 @@ use std::cmp::Ordering;
 use std::convert::identity;
 use std::iter::FromIterator;
 
-pub trait IteratorMaxItems : Iterator + Sized {
+pub trait IteratorMaxItems: Iterator + Sized {
     fn max_items(self) -> Vec<Self::Item>
     where
         Self::Item: Ord,
@@ -39,11 +39,7 @@ pub trait IteratorMaxItems : Iterator + Sized {
             move |(_, x)| g(x)
         }
 
-        self.map(key(f))
-            .max_items_by(compare)
-            .into_iter()
-            .map(snd_map(g))
-            .collect()
+        self.map(key(f)).max_items_by(compare).into_iter().map(snd_map(g)).collect()
     }
 
     fn max_items_by<F>(self, compare: F) -> Vec<Self::Item>
@@ -59,7 +55,10 @@ pub trait IteratorMaxItems : Iterator + Sized {
                 };
                 match ordering {
                     Ordering::Greater => vec![t],
-                    Ordering::Equal => {vec.push(t); vec},
+                    Ordering::Equal => {
+                        vec.push(t);
+                        vec
+                    }
                     Ordering::Less => vec,
                 }
             }
@@ -69,7 +68,7 @@ pub trait IteratorMaxItems : Iterator + Sized {
     }
 }
 
-impl<T> IteratorMaxItems for T where T: Iterator{}
+impl<T> IteratorMaxItems for T where T: Iterator {}
 
 #[cfg(test)]
 mod tests {
@@ -98,15 +97,13 @@ mod tests {
 
     #[test]
     fn multiple_max_items_by_key() {
-        let result: Vec<_> = [("a", 5), ("b", 4), ("c", 3), ("d", 2), ("e", 5)].iter()
-            .max_items_by_key(|&&(_, x)| x);
+        let result: Vec<_> = [("a", 5), ("b", 4), ("c", 3), ("d", 2), ("e", 5)].iter().max_items_by_key(|&&(_, x)| x);
         assert_eq!(vec![&("a", 5), &("e", 5)], result);
     }
 
     #[test]
     fn multiple_max_items_by_key_map() {
-        let result: Vec<_> = [("a", 5), ("b", 4), ("c", 3), ("d", 2), ("e", 5)].iter()
-            .max_items_by_key_map(|&&(_, x)| x, |&(s, _)| s);
+        let result: Vec<_> = [("a", 5), ("b", 4), ("c", 3), ("d", 2), ("e", 5)].iter().max_items_by_key_map(|&&(_, x)| x, |&(s, _)| s);
         assert_eq!(vec!["a", "e"], result);
     }
 }
index 9f5e5546919e9d17b6c04c108e0172c4326fdf9b..7b7f8e062f4b4d0aea93f00f1a09e244dbf90e0b 100644 (file)
@@ -1,6 +1,6 @@
 use std::fmt::{self, Display, Formatter};
 
-use redis::{FromRedisValue, Value, RedisResult, ToRedisArgs, RedisWrite, NumericBehavior, RedisError, ErrorKind};
+use redis::{ErrorKind, FromRedisValue, NumericBehavior, RedisError, RedisResult, RedisWrite, ToRedisArgs, Value};
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
 #[serde(transparent)]
@@ -28,7 +28,7 @@ impl FromRedisValue for Timestamp {
                     Ok(Timestamp(secs * 1000 + micros / 1000))
                 }
                 _ => Err(RedisError::from((ErrorKind::TypeError, "Expected exactly 2 integers, secs and micros"))),
-            }
+            },
             _ => Err(RedisError::from((ErrorKind::TypeError, "Expected exactly 2 integers, secs and micros"))),
         }
     }
@@ -36,7 +36,8 @@ impl FromRedisValue for Timestamp {
 
 impl ToRedisArgs for Timestamp {
     fn write_redis_args<W>(&self, out: &mut W)
-        where W: ?Sized + RedisWrite
+    where
+        W: ?Sized + RedisWrite,
     {
         self.0.write_redis_args(out)
     }
@@ -56,10 +57,7 @@ mod tests {
 
     #[test]
     fn from_redis_time_command() {
-        let value = Value::Bulk(vec![
-            Value::Data(b"1614792013".to_vec()),
-            Value::Data(b"938726".to_vec()),
-        ]);
+        let value = Value::Bulk(vec![Value::Data(b"1614792013".to_vec()), Value::Data(b"938726".to_vec())]);
         let timestamp = Timestamp::from_redis_value(&value).unwrap();
         assert_eq!(Timestamp(1614792013938), timestamp);
     }