From 21b7b57336cd3bf7bd328ada38c6069a48504f85 Mon Sep 17 00:00:00 2001 From: Kjetil Orbekk Date: Tue, 27 Dec 2022 19:55:10 -0500 Subject: `cargo fmt` --- protocol/src/bot.rs | 5 +- protocol/src/bridge_engine.rs | 29 ++-- protocol/src/card.rs | 61 +++++-- protocol/src/lib.rs | 12 +- protocol/src/play_result.rs | 2 +- protocol/src/simple_bots.rs | 5 +- server/src/auth.rs | 96 ++++++----- server/src/error.rs | 11 +- server/src/fake_auth.rs | 14 +- server/src/main.rs | 2 +- server/src/play.rs | 19 ++- sqlx-data.json | 225 ++++++++++++++++++++++++++ webapp/src/components/app_context_provider.rs | 86 +++++----- webapp/src/components/bidding.rs | 2 +- webapp/src/components/bidding_box.rs | 2 +- webapp/src/components/bidding_table.rs | 2 +- webapp/src/components/card.rs | 2 +- webapp/src/components/hand.rs | 2 +- webapp/src/components/table.rs | 6 +- webapp/src/routing.rs | 3 +- webapp/src/services.rs | 28 ++-- webapp/src/utils.rs | 10 +- 22 files changed, 477 insertions(+), 147 deletions(-) create mode 100644 sqlx-data.json diff --git a/protocol/src/bot.rs b/protocol/src/bot.rs index 250731e..a85fda0 100644 --- a/protocol/src/bot.rs +++ b/protocol/src/bot.rs @@ -1,6 +1,9 @@ use async_trait::async_trait; -use crate::{bridge_engine::{BiddingStatePlayerView, Bid, PlayStatePlayerView}, card::Card}; +use crate::{ + bridge_engine::{Bid, BiddingStatePlayerView, PlayStatePlayerView}, + card::Card, +}; #[async_trait] pub trait BiddingBot { diff --git a/protocol/src/bridge_engine.rs b/protocol/src/bridge_engine.rs index 37687b8..0bc3434 100644 --- a/protocol/src/bridge_engine.rs +++ b/protocol/src/bridge_engine.rs @@ -109,10 +109,12 @@ impl Trick { } }; - let (i, _) = self.cards_played + let (i, _) = self + .cards_played .iter() .enumerate() - .max_by(|&(_, c1), &(_, c2)| value(c1).cmp(&value(c2))).unwrap(); + .max_by(|&(_, c1), &(_, c2)| value(c1).cmp(&value(c2))) + .unwrap(); self.leader.many_next(i) } } @@ -148,7 +150,10 @@ impl TurnInPlay { &self.trick.cards_played[..] } - pub fn play(mut self: TurnInPlay, card: Card) -> MoveResult { + pub fn play( + mut self: TurnInPlay, + card: Card, + ) -> MoveResult { self.trick.cards_played.push(card); if self.trick.cards_played.len() >= 4 { return MoveResult::Next(self.trick); @@ -231,12 +236,10 @@ impl DealInPlay { player_cards.remove(i); Ok(match self.in_progress.play(card) { - MoveResult::Current(turn) => { - MoveResult::Current(Self { - in_progress: turn, - ..self - }) - } + MoveResult::Current(turn) => MoveResult::Current(Self { + in_progress: turn, + ..self + }), MoveResult::Next(trick) => { let trick_winner = trick.winner(self.trump_suit); let mut tricks = self.tricks_played; @@ -681,9 +684,7 @@ impl PlayState { ..self }) } - MoveResult::Next(_) => { - PlayStateResult::PlayFinished(PlayResult) - } + MoveResult::Next(_) => PlayStateResult::PlayFinished(PlayResult), }) } } @@ -1307,7 +1308,9 @@ mod tests { assert_eq!(player_state.current_player(), play_state.current_player()); } - fn as_playing_hand(result: MoveResult>) -> DealInPlay { + fn as_playing_hand( + result: MoveResult>, + ) -> DealInPlay { match result { MoveResult::Current(r) => r, MoveResult::Next(_) => { diff --git a/protocol/src/card.rs b/protocol/src/card.rs index b6b57e7..30cf822 100644 --- a/protocol/src/card.rs +++ b/protocol/src/card.rs @@ -8,7 +8,16 @@ use strum_macros::EnumCount; use strum_macros::EnumIter; #[derive( - PartialOrd, Ord, PartialEq, Eq, Clone, Copy, EnumIter, EnumCount, Serialize, Deserialize, + PartialOrd, + Ord, + PartialEq, + Eq, + Clone, + Copy, + EnumIter, + EnumCount, + Serialize, + Deserialize, )] pub enum Suit { Club, @@ -17,7 +26,17 @@ pub enum Suit { Spade, } -#[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Copy, EnumIter, Serialize, Deserialize)] +#[derive( + PartialOrd, + Ord, + PartialEq, + Eq, + Clone, + Copy, + EnumIter, + Serialize, + Deserialize, +)] pub enum Rank { Two = 2, Three, @@ -35,7 +54,10 @@ pub enum Rank { } impl fmt::Display for Suit { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { + fn fmt( + &self, + f: &mut std::fmt::Formatter<'_>, + ) -> std::result::Result<(), std::fmt::Error> { f.write_str(match self { Suit::Club => "♣", Suit::Diamond => "♢", @@ -66,13 +88,19 @@ impl std::str::FromStr for Suit { } impl fmt::Debug for Suit { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { + fn fmt( + &self, + f: &mut std::fmt::Formatter<'_>, + ) -> std::result::Result<(), std::fmt::Error> { write!(f, "{}", self) } } impl fmt::Display for Rank { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { + fn fmt( + &self, + f: &mut std::fmt::Formatter<'_>, + ) -> std::result::Result<(), std::fmt::Error> { f.write_str(match self { Rank::Ace => "A", Rank::King => "K", @@ -92,7 +120,10 @@ impl fmt::Display for Rank { } impl fmt::Debug for Rank { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { + fn fmt( + &self, + f: &mut std::fmt::Formatter<'_>, + ) -> std::result::Result<(), std::fmt::Error> { write!(f, "{}", self) } } @@ -125,19 +156,29 @@ impl std::str::FromStr for Rank { pub struct Card(pub Suit, pub Rank); impl Card { - pub fn suit(&self) -> Suit { self.0 } - pub fn rank(&self) -> Rank { self.1 } + pub fn suit(&self) -> Suit { + self.0 + } + pub fn rank(&self) -> Rank { + self.1 + } } impl fmt::Display for Card { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { + fn fmt( + &self, + f: &mut std::fmt::Formatter<'_>, + ) -> std::result::Result<(), std::fmt::Error> { let Card(suit, rank) = self; write!(f, "{}{}", suit, rank) } } impl fmt::Debug for Card { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { + fn fmt( + &self, + f: &mut std::fmt::Formatter<'_>, + ) -> std::result::Result<(), std::fmt::Error> { write!(f, "{}", self) } } diff --git a/protocol/src/lib.rs b/protocol/src/lib.rs index 3fbf1e0..8ae9a36 100644 --- a/protocol/src/lib.rs +++ b/protocol/src/lib.rs @@ -1,10 +1,10 @@ use serde::{Deserialize, Serialize}; use uuid::Uuid; -pub mod card; -pub mod bridge_engine; pub mod bot; -pub mod simple_bots; +pub mod bridge_engine; +pub mod card; pub mod play_result; +pub mod simple_bots; #[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)] pub struct UserInfo { @@ -23,6 +23,10 @@ mod tests { pub fn test_setup() { dotenv::dotenv().ok(); - let _ =env_logger::Builder::from_env(Env::default().default_filter_or("info")).is_test(true).try_init(); + let _ = env_logger::Builder::from_env( + Env::default().default_filter_or("info"), + ) + .is_test(true) + .try_init(); } } diff --git a/protocol/src/play_result.rs b/protocol/src/play_result.rs index 2dba276..8dad6ef 100644 --- a/protocol/src/play_result.rs +++ b/protocol/src/play_result.rs @@ -1,4 +1,4 @@ -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)] pub enum MoveResult { diff --git a/protocol/src/simple_bots.rs b/protocol/src/simple_bots.rs index 8612e64..2c2832c 100644 --- a/protocol/src/simple_bots.rs +++ b/protocol/src/simple_bots.rs @@ -162,7 +162,10 @@ mod tests { let mut result = PlayStateResult::InProgress(example_play_state()); while let PlayStateResult::InProgress(play_state) = result { info!("Play state: {play_state:#?}"); - let player_state = PlayStatePlayerView::from_play_state(&play_state, play_state.current_player()); + let player_state = PlayStatePlayerView::from_play_state( + &play_state, + play_state.current_player(), + ); let card = bot.play(&player_state).await; result = play_state.play(card).unwrap(); } diff --git a/server/src/auth.rs b/server/src/auth.rs index a924f44..2831929 100644 --- a/server/src/auth.rs +++ b/server/src/auth.rs @@ -6,21 +6,27 @@ use std::{ sync::{Arc, Mutex}, }; -use crate::{error::BridgeError, server::{ServerState, ServerContext}}; +use crate::{ + error::BridgeError, + server::{ServerContext, ServerState}, +}; use async_trait::async_trait; use axum::{ - extract::{FromRequestParts, State, FromRef}, - response::{IntoResponse, Response}, http::request::Parts, + extract::{FromRef, FromRequestParts, State}, + http::request::Parts, + response::{IntoResponse, Response}, }; use chrono::{DateTime, Utc}; use lru::LruCache; use openidconnect::{ - core::{CoreClient, CoreProviderMetadata, CoreResponseType, CoreUserInfoClaims}, + core::{ + CoreClient, CoreProviderMetadata, CoreResponseType, CoreUserInfoClaims, + }, reqwest::async_http_client, url::Url, - AccessToken, AccessTokenHash, AuthenticationFlow, AuthorizationCode, ClientId, ClientSecret, - CsrfToken, IssuerUrl, Nonce, OAuth2TokenResponse, RedirectUrl, RefreshToken, Scope, - TokenResponse, + AccessToken, AccessTokenHash, AuthenticationFlow, AuthorizationCode, + ClientId, ClientSecret, CsrfToken, IssuerUrl, Nonce, OAuth2TokenResponse, + RedirectUrl, RefreshToken, Scope, TokenResponse, }; use serde::{Deserialize, Serialize}; use sqlx::PgPool; @@ -62,7 +68,10 @@ impl AuthenticatedSession { #[async_trait] pub trait Authenticator { - async fn user_info(&self, session: &mut AuthenticatedSession) -> Result; + async fn user_info( + &self, + session: &mut AuthenticatedSession, + ) -> Result; async fn authenticate( &self, pool: &PgPool, @@ -121,14 +130,18 @@ impl OauthAuthenticator { redirect_uri: RedirectUrl, ) -> Self { // Use OpenID Connect Discovery to fetch the provider metadata. - let provider_metadata = CoreProviderMetadata::discover_async(issuer_url, async_http_client) - .await - .unwrap(); - - let client = - CoreClient::from_provider_metadata(provider_metadata, client_id, Some(client_secret)) - // Set the URL the user will be redirected to after the authorization process. - .set_redirect_uri(redirect_uri); + let provider_metadata = + CoreProviderMetadata::discover_async(issuer_url, async_http_client) + .await + .unwrap(); + + let client = CoreClient::from_provider_metadata( + provider_metadata, + client_id, + Some(client_secret), + ) + // Set the URL the user will be redirected to after the authorization process. + .set_redirect_uri(redirect_uri); Self { db, @@ -168,9 +181,12 @@ impl OauthAuthenticator { debug!("Got new token: {new_token:#?}"); // TODO: Validate token? if let Some(expires_in) = new_token.expires_in() { - session.expiration = refresh_start + chrono::Duration::from_std(expires_in)?; + session.expiration = + refresh_start + chrono::Duration::from_std(expires_in)?; } else { - error!("Token is missing expiration! Will refresh token every time."); + error!( + "Token is missing expiration! Will refresh token every time." + ); } if let Some(refresh_token) = new_token.refresh_token() { session.refresh_token = refresh_token.clone(); @@ -202,7 +218,10 @@ impl Authenticator for OauthAuthenticator { (user_id, auth_url) } - async fn user_info(&self, session: &mut AuthenticatedSession) -> Result { + async fn user_info( + &self, + session: &mut AuthenticatedSession, + ) -> Result { self.maybe_refresh_token(session).await?; let user_info: CoreUserInfoClaims = self .client @@ -225,12 +244,10 @@ impl Authenticator for OauthAuthenticator { auth_params: HashMap, ) -> Result { // TODO: If the token is missing from the cache, client should retry logging in. - let state = self - .login_cache - .lock() - .unwrap() - .pop(&session_id) - .ok_or(BridgeError::InvalidRequest("token missing".to_string()))?; + let state = + self.login_cache.lock().unwrap().pop(&session_id).ok_or( + BridgeError::InvalidRequest("token missing".to_string()), + )?; if Some(state.csrf_token.secret()) != auth_params.get("state") { return Err(BridgeError::InvalidRequest( "token validation failed".to_string(), @@ -254,12 +271,15 @@ impl Authenticator for OauthAuthenticator { let id_token = token.id_token().ok_or(BridgeError::InvalidRequest( "Server did not return an IdToken".to_string(), ))?; - let claims = id_token.claims(&self.client.id_token_verifier(), &state.nonce)?; + let claims = + id_token.claims(&self.client.id_token_verifier(), &state.nonce)?; // Verify access token hash. if let Some(expected_access_token_hash) = claims.access_token_hash() { - let actual_access_token_hash = - AccessTokenHash::from_token(token.access_token(), &id_token.signing_alg()?)?; + let actual_access_token_hash = AccessTokenHash::from_token( + token.access_token(), + &id_token.signing_alg()?, + )?; if actual_access_token_hash != *expected_access_token_hash { return Err(BridgeError::InvalidRequest( "Invalid access token".to_string(), @@ -274,9 +294,9 @@ impl Authenticator for OauthAuthenticator { ))); } - let refresh_token = token - .refresh_token() - .ok_or(BridgeError::Internal("Expected refresh token".to_string()))?; + let refresh_token = token.refresh_token().ok_or( + BridgeError::Internal("Expected refresh token".to_string()), + )?; let mut session = AuthenticatedSession { player_id: claims.subject().to_string(), @@ -371,12 +391,13 @@ pub async fn fetch_authenticated_session( impl FromRequestParts for AuthenticatedSession where S: Send + Sync, - Arc: FromRef + Arc: FromRef, { type Rejection = Response; async fn from_request_parts( - parts: &mut Parts, state: &S + parts: &mut Parts, + state: &S, ) -> Result { let cookies = Cookies::from_request_parts(parts, state) .await @@ -395,9 +416,12 @@ where } Ok(s) => s, }; - let session = match crate::auth::fetch_authenticated_session(&state.db, &session_id) - .await - .map_err(|e| e.into_response())? + let session = match crate::auth::fetch_authenticated_session( + &state.db, + &session_id, + ) + .await + .map_err(|e| e.into_response())? { None => return Err(BridgeError::NotLoggedIn.into_response()), Some(v) => v, diff --git a/server/src/error.rs b/server/src/error.rs index b6a7c4d..7eb5fa2 100644 --- a/server/src/error.rs +++ b/server/src/error.rs @@ -2,10 +2,13 @@ use axum::{ http::{self, StatusCode}, response::IntoResponse, }; -use openidconnect::{core::CoreErrorResponseType, ClaimsVerificationError, StandardErrorResponse}; +use openidconnect::{ + core::CoreErrorResponseType, ClaimsVerificationError, StandardErrorResponse, +}; use tracing::error; -type UserInfoError = openidconnect::UserInfoError>; +type UserInfoError = + openidconnect::UserInfoError>; type RequestTokenError = openidconnect::RequestTokenError< openidconnect::reqwest::Error, @@ -36,7 +39,7 @@ pub enum BridgeError { NotLoggedIn, #[error("Anyhow error")] - AnyhowError(#[from]anyhow::Error), + AnyhowError(#[from] anyhow::Error), #[error("Authentication error")] SigningFailed(#[from] openidconnect::SigningError), @@ -68,7 +71,7 @@ impl BridgeError { match self { BridgeError::NotLoggedIn => { (StatusCode::UNAUTHORIZED, format!("Must be logged in")) - }, + } BridgeError::OpenidRequestTokenError(_) => { (StatusCode::UNAUTHORIZED, format!("Error fetching token")) } diff --git a/server/src/fake_auth.rs b/server/src/fake_auth.rs index c0574af..6a3ff2c 100644 --- a/server/src/fake_auth.rs +++ b/server/src/fake_auth.rs @@ -6,11 +6,14 @@ use std::{ use async_trait::async_trait; use chrono::Utc; -use openidconnect::{RefreshToken, AccessToken}; +use openidconnect::{AccessToken, RefreshToken}; use reqwest::Url; use crate::{ - auth::{AuthenticatedSession, Authenticator, SessionId, store_authenticated_session}, + auth::{ + store_authenticated_session, AuthenticatedSession, Authenticator, + SessionId, + }, error::{self, BridgeError}, }; @@ -70,8 +73,11 @@ impl Authenticator for FakeAuthenticator { } async fn get_login_url(&self) -> (SessionId, reqwest::Url) { - let auth_url = - Url::parse(&format!("{}/api/fake_login", env::var("APP_URL").unwrap())).unwrap(); + let auth_url = Url::parse(&format!( + "{}/api/fake_login", + env::var("APP_URL").unwrap() + )) + .unwrap(); let user_id = SessionId::new(); self.login_cache .lock() diff --git a/server/src/main.rs b/server/src/main.rs index c0ec4cb..9034b67 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -61,7 +61,7 @@ async fn create_authenticator( async fn create_authenticator( db_pool: &PgPool, ) -> Box { - create_default_authenticator(db_pool) + create_default_authenticator(db_pool).await } #[tokio::main] diff --git a/server/src/play.rs b/server/src/play.rs index 68ea94b..d15d64c 100644 --- a/server/src/play.rs +++ b/server/src/play.rs @@ -157,9 +157,7 @@ impl> Table { pub fn result(&self) -> Result<&GameResult, BridgeError> { match &self.state { TableState::Result(r) => Ok(r), - _ => Err(BridgeError::InvalidRequest( - "no result".to_string(), - )), + _ => Err(BridgeError::InvalidRequest("no result".to_string())), } } @@ -183,9 +181,13 @@ impl> Table { let dealer = match &self.state { TableState::Unknown => random(), TableState::Game(g) => g.dealer(), - TableState::Result(_) => random(), // TODO: We should know the dealer here. + TableState::Result(_) => random(), // TODO: We should know the dealer here. }; - self.insert_and_apply(TableUpdate::NewDeal { deal: random(), dealer: dealer.next() }).await + self.insert_and_apply(TableUpdate::NewDeal { + deal: random(), + dealer: dealer.next(), + }) + .await } async fn insert_and_apply( @@ -216,7 +218,7 @@ impl> Table { TableUpdate::Play(card) => { self.state = self.game()?.clone().play(card)?.into(); Ok(()) - }, + } } } @@ -292,7 +294,10 @@ pub async fn advance_play>( #[cfg(test)] mod test { - use protocol::{bridge_engine::{Raise, ContractLevel}, card::Suit}; + use protocol::{ + bridge_engine::{ContractLevel, Raise}, + card::Suit, + }; use serde_json::json; use tracing::info; diff --git a/sqlx-data.json b/sqlx-data.json new file mode 100644 index 0000000..a0a5c19 --- /dev/null +++ b/sqlx-data.json @@ -0,0 +1,225 @@ +{ + "db": "PostgreSQL", + "05a3ad4f0b6c1c34d120e2fbf37927259ffbcac0b6b4c6e9dc5864cef9ce6640": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Left": [ + "Uuid", + "Varchar" + ] + } + }, + "query": "\n insert into table_players (active_tables_id,\n player_id,\n position)\n values ($1, $2, 'south')\n " + }, + "26fc6af83759bf876a88aebcdf36b5282bdd354f09a7ecdc2e21f9418874ae41": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Left": [ + "Text" + ] + } + }, + "query": "\n delete from table_players where player_id = $1\n " + }, + "3e270c5c0bb37186f0cd75d3197413611f7e2ad83a8ccbe31a4c463d69ee9d4c": { + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Uuid" + }, + { + "name": "player_id", + "ordinal": 1, + "type_info": "Varchar" + }, + { + "name": "access_token", + "ordinal": 2, + "type_info": "Varchar" + }, + { + "name": "access_token_expiration", + "ordinal": 3, + "type_info": "Timestamptz" + }, + { + "name": "refresh_token", + "ordinal": 4, + "type_info": "Varchar" + }, + { + "name": "last_refresh", + "ordinal": 5, + "type_info": "Timestamptz" + } + ], + "nullable": [ + false, + false, + false, + false, + false, + false + ], + "parameters": { + "Left": [ + "Uuid", + "Varchar", + "Varchar", + "Timestamptz", + "Varchar" + ] + } + }, + "query": "\n insert into sessions (\n id,\n player_id,\n access_token,\n access_token_expiration,\n refresh_token\n ) values ($1, $2, $3, $4, $5)\n on conflict (id) do update set\n access_token = EXCLUDED.access_token,\n access_token_expiration = EXCLUDED.access_token_expiration,\n refresh_token = EXCLUDED.refresh_token,\n last_refresh = now()\n returning *\n " + }, + "48a1d60ab96fc82fa4064fffeb0ab71689e9641e9cf8f99232048e9ee354ec55": { + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Uuid" + }, + { + "name": "player_id", + "ordinal": 1, + "type_info": "Varchar" + }, + { + "name": "access_token", + "ordinal": 2, + "type_info": "Varchar" + }, + { + "name": "access_token_expiration", + "ordinal": 3, + "type_info": "Timestamptz" + }, + { + "name": "refresh_token", + "ordinal": 4, + "type_info": "Varchar" + }, + { + "name": "last_refresh", + "ordinal": 5, + "type_info": "Timestamptz" + } + ], + "nullable": [ + false, + false, + false, + false, + false, + false + ], + "parameters": { + "Left": [ + "Uuid" + ] + } + }, + "query": "\n select * from sessions\n where id = $1\n " + }, + "9c334e7646337f746e885253c8942750ed49ddb7fb0860c75afa6f8430dbc560": { + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Uuid" + } + ], + "nullable": [ + false + ], + "parameters": { + "Left": [ + "Uuid" + ] + } + }, + "query": "\n insert into active_tables (id)\n values ($1)\n returning id\n " + }, + "b87f3b68e682f1db7552db6b18e9fb2ba208a47b526cc764f740b9c687d75992": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Left": [ + "Varchar" + ] + } + }, + "query": "\n insert into players (id)\n values ($1)\n on conflict do nothing\n " + }, + "bab6adb0a18c6dcb8cd61b19c84347a2495c09fa12229299d6a64c1a01a38395": { + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Uuid" + } + ], + "nullable": [ + false + ], + "parameters": { + "Left": [ + "Text" + ] + } + }, + "query": "\n select tables.id\n from table_players players\n natural join active_tables tables\n where player_id = $1\n " + }, + "c343b2efe469200c56d080c82caead4a6ca48a344dde344561de81ebf6343747": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Left": [ + "Uuid", + "Int8", + "Jsonb" + ] + } + }, + "query": "\n insert into object_journal (id, seq, payload)\n values ($1, $2, $3)\n " + }, + "e37e47e3fbd6e06006280687bd73a7bb55a2847bf76deb284b8373b93c479fcd": { + "describe": { + "columns": [ + { + "name": "seq", + "ordinal": 0, + "type_info": "Int8" + }, + { + "name": "payload: _", + "ordinal": 1, + "type_info": "Jsonb" + } + ], + "nullable": [ + false, + false + ], + "parameters": { + "Left": [ + "Uuid", + "Int8" + ] + } + }, + "query": "\n select seq, payload as \"payload: _\" from object_journal\n where id = $1 and seq >= $2\n order by seq\n " + } +} \ No newline at end of file diff --git a/webapp/src/components/app_context_provider.rs b/webapp/src/components/app_context_provider.rs index 6298c2b..081026c 100644 --- a/webapp/src/components/app_context_provider.rs +++ b/webapp/src/components/app_context_provider.rs @@ -1,14 +1,14 @@ -use log::error; use crate::{routing::Route, utils::ok_json}; +use anyhow::Context; use gloo_net::http::Request; +use log::error; use log::info; use protocol::UserInfo; -use uuid::Uuid; use std::future::Future; +use uuid::Uuid; use wasm_bindgen_futures::spawn_local; use yew::prelude::*; use yew_router::prelude::*; -use anyhow::Context; #[derive(Properties, Clone, PartialEq, Debug)] pub struct ErrorInfoProperties { @@ -35,7 +35,7 @@ impl AppContext { let error = self.state.error.clone(); spawn_local(async move { if let Err(err) = f.await { - error!("Error occurred: {err:?}"); + error!("Error occurred: {err:?}"); error.set(Some(ErrorInfoProperties { message: format!("{err:?}"), })); @@ -58,40 +58,41 @@ impl AppContext { } pub fn create_table(&self) { - let user = self.state.user.clone(); - let history = self.history.clone(); - self.spawn_async(async move { - let response = Request::post("/api/table").send().await?; - let table_id: Uuid = ok_json(response).await.context("creating table")?; + let user = self.state.user.clone(); + let history = self.history.clone(); + self.spawn_async(async move { + let response = Request::post("/api/table").send().await?; + let table_id: Uuid = + ok_json(response).await.context("creating table")?; info!("Created table {table_id}"); - if let Some(user_info) = user.as_ref() { - user.set(Some(UserInfo { - table: Some(protocol::Table { id: table_id }), - ..(user_info.clone()) - })); - } - history.push(Route::Home); - Ok(()) - }); + if let Some(user_info) = user.as_ref() { + user.set(Some(UserInfo { + table: Some(protocol::Table { id: table_id }), + ..(user_info.clone()) + })); + } + history.push(Route::Home); + Ok(()) + }); } pub fn leave_table(&self) { - let user = self.state.user.clone(); - let history = self.history.clone(); - self.spawn_async(async move { - let response = Request::delete("/api/table").send().await?; - if !response.ok() { - anyhow::bail!("error while leaving table"); - } - if let Some(user_info) = user.as_ref() { - user.set(Some(UserInfo { - table: None, - ..(user_info.clone()) - })); - } - history.push(Route::Home); - Ok(()) - }); + let user = self.state.user.clone(); + let history = self.history.clone(); + self.spawn_async(async move { + let response = Request::delete("/api/table").send().await?; + if !response.ok() { + anyhow::bail!("error while leaving table"); + } + if let Some(user_info) = user.as_ref() { + user.set(Some(UserInfo { + table: None, + ..(user_info.clone()) + })); + } + history.push(Route::Home); + Ok(()) + }); } } @@ -101,7 +102,10 @@ pub struct Props { } async fn initialize_user_info() -> Result, anyhow::Error> { - let response = Request::get("/api/user/info").send().await.context("fetching user_info")?; + let response = Request::get("/api/user/info") + .send() + .await + .context("fetching user_info")?; if response.status() == 401 { web_sys::window() .unwrap() @@ -113,7 +117,7 @@ async fn initialize_user_info() -> Result, anyhow::Error> { } pub fn use_app_context() -> AppContext { - let state : AppState = use_context::().unwrap(); + let state: AppState = use_context::().unwrap(); let history = use_history().unwrap(); AppContext { state, history } @@ -136,7 +140,10 @@ pub fn app_context_provider(props: &Props) -> Html { match initialize_user_info().await { Ok(user_info) => user.set(user_info), Err(e) => error.set(Some(ErrorInfoProperties { - message: format!("Could not contact server: {:?}", e), + message: format!( + "Could not contact server: {:?}", + e + ), })), }; }); @@ -155,10 +162,7 @@ pub fn app_context_provider(props: &Props) -> Html { info!("Recomputing state"); info!("User is {:?}", *user); - let state = AppState { - user, - error, - }; + let state = AppState { user, error }; html! { context={state}> diff --git a/webapp/src/components/bidding.rs b/webapp/src/components/bidding.rs index 2c48ca3..9b92930 100644 --- a/webapp/src/components/bidding.rs +++ b/webapp/src/components/bidding.rs @@ -1,6 +1,6 @@ -use protocol::bridge_engine::{self, BiddingResult, Contract, Player}; use crate::components::{BiddingBox, BiddingTable}; use log::error; +use protocol::bridge_engine::{self, BiddingResult, Contract, Player}; use yew::prelude::*; #[derive(PartialEq, Properties, Clone)] diff --git a/webapp/src/components/bidding_box.rs b/webapp/src/components/bidding_box.rs index 0b384ec..0b660ea 100644 --- a/webapp/src/components/bidding_box.rs +++ b/webapp/src/components/bidding_box.rs @@ -1,5 +1,5 @@ -use protocol::bridge_engine::{Bid, Raise}; use crate::components::bid_css_class; +use protocol::bridge_engine::{Bid, Raise}; use yew::prelude::*; #[function_component(BiddingBox)] diff --git a/webapp/src/components/bidding_table.rs b/webapp/src/components/bidding_table.rs index 0f1a824..161f85a 100644 --- a/webapp/src/components/bidding_table.rs +++ b/webapp/src/components/bidding_table.rs @@ -1,5 +1,5 @@ -use protocol::bridge_engine::{Bid, Bidding, Player}; use crate::components::bid_css_class; +use protocol::bridge_engine::{Bid, Bidding, Player}; use yew::prelude::*; #[function_component(BiddingTable)] diff --git a/webapp/src/components/card.rs b/webapp/src/components/card.rs index 48d53e3..82d4fcb 100644 --- a/webapp/src/components/card.rs +++ b/webapp/src/components/card.rs @@ -1,5 +1,5 @@ -use protocol::card; use crate::components::suit_css_class; +use protocol::card; use yew::prelude::*; #[function_component(Card)] diff --git a/webapp/src/components/hand.rs b/webapp/src/components/hand.rs index 7946a5c..7d97ffd 100644 --- a/webapp/src/components/hand.rs +++ b/webapp/src/components/hand.rs @@ -1,5 +1,5 @@ -use protocol::card; use crate::components::card::Card; +use protocol::card; use yew::prelude::*; #[function_component(Hand)] diff --git a/webapp/src/components/table.rs b/webapp/src/components/table.rs index bd49b10..6d35613 100644 --- a/webapp/src/components/table.rs +++ b/webapp/src/components/table.rs @@ -149,9 +149,11 @@ impl Component for OnlineTableInner { async move { services::new_deal(table.clone()).await?; services::get_table_player_view(table).await - }.map(Msg::TableStateUpdated)); + } + .map(Msg::TableStateUpdated), + ); false - }, + } } } diff --git a/webapp/src/routing.rs b/webapp/src/routing.rs index 57d4af0..b1e6e01 100644 --- a/webapp/src/routing.rs +++ b/webapp/src/routing.rs @@ -1,5 +1,5 @@ -use yew_router::prelude::*; use uuid::Uuid; +use yew_router::prelude::*; #[derive(Clone, Routable, PartialEq)] pub enum Route { @@ -8,4 +8,3 @@ pub enum Route { #[at("/table/:id")] Table { id: Uuid }, } - diff --git a/webapp/src/services.rs b/webapp/src/services.rs index 668530f..cd0c649 100644 --- a/webapp/src/services.rs +++ b/webapp/src/services.rs @@ -1,6 +1,10 @@ use anyhow::Context; use gloo_net::http::Request; -use protocol::{bridge_engine::{TableStatePlayerView, Bid}, Table, card::Card}; +use protocol::{ + bridge_engine::{Bid, TableStatePlayerView}, + card::Card, + Table, +}; use crate::utils::ok_json; @@ -16,22 +20,20 @@ pub async fn get_table_player_view( } pub async fn bid(table: Table, bid: Bid) -> Result<(), anyhow::Error> { - let response = - Request::post(&format!("/api/table/{}/bid", table.id)) - .json(&bid)? - .send() - .await - .context("submitting bid")?; + let response = Request::post(&format!("/api/table/{}/bid", table.id)) + .json(&bid)? + .send() + .await + .context("submitting bid")?; ok_json(response).await } pub async fn play(table: Table, card: Card) -> Result<(), anyhow::Error> { - let response = - Request::post(&format!("/api/table/{}/play", table.id)) - .json(&card)? - .send() - .await - .context("submitting play")?; + let response = Request::post(&format!("/api/table/{}/play", table.id)) + .json(&card)? + .send() + .await + .context("submitting play")?; ok_json(response).await } diff --git a/webapp/src/utils.rs b/webapp/src/utils.rs index 1eadf2f..86493eb 100644 --- a/webapp/src/utils.rs +++ b/webapp/src/utils.rs @@ -2,9 +2,15 @@ use gloo_net::http::Response; use serde::de::DeserializeOwned; pub async fn ok_json(response: Response) -> Result -where T: DeserializeOwned { +where + T: DeserializeOwned, +{ if !response.ok() { - anyhow::bail!("Request failed: {} {}", response.status(), response.status_text()); + anyhow::bail!( + "Request failed: {} {}", + response.status(), + response.status_text() + ); } Ok(response.json().await?) } -- cgit v1.2.3