summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKjetil Orbekk <kj@orbekk.com>2022-12-27 19:55:10 -0500
committerKjetil Orbekk <kj@orbekk.com>2022-12-27 19:58:45 -0500
commit21b7b57336cd3bf7bd328ada38c6069a48504f85 (patch)
treec0d47337f584403febcfc9ec365ea1ea810e782e
parent3418ebf4db2375e0dfe2343da57d674e1f4fd57f (diff)
`cargo fmt`
-rw-r--r--protocol/src/bot.rs5
-rw-r--r--protocol/src/bridge_engine.rs29
-rw-r--r--protocol/src/card.rs61
-rw-r--r--protocol/src/lib.rs12
-rw-r--r--protocol/src/play_result.rs2
-rw-r--r--protocol/src/simple_bots.rs5
-rw-r--r--server/src/auth.rs96
-rw-r--r--server/src/error.rs11
-rw-r--r--server/src/fake_auth.rs14
-rw-r--r--server/src/main.rs2
-rw-r--r--server/src/play.rs19
-rw-r--r--sqlx-data.json225
-rw-r--r--webapp/src/components/app_context_provider.rs86
-rw-r--r--webapp/src/components/bidding.rs2
-rw-r--r--webapp/src/components/bidding_box.rs2
-rw-r--r--webapp/src/components/bidding_table.rs2
-rw-r--r--webapp/src/components/card.rs2
-rw-r--r--webapp/src/components/hand.rs2
-rw-r--r--webapp/src/components/table.rs6
-rw-r--r--webapp/src/routing.rs3
-rw-r--r--webapp/src/services.rs28
-rw-r--r--webapp/src/utils.rs10
22 files changed, 477 insertions, 147 deletions
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<TurnInPlay, Trick> {
+ pub fn play(
+ mut self: TurnInPlay,
+ card: Card,
+ ) -> MoveResult<TurnInPlay, Trick> {
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, Vec<Trick>>) -> DealInPlay {
+ fn as_playing_hand(
+ result: MoveResult<DealInPlay, Vec<Trick>>,
+ ) -> 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<Current, Next> {
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<String, BridgeError>;
+ async fn user_info(
+ &self,
+ session: &mut AuthenticatedSession,
+ ) -> Result<String, BridgeError>;
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<String, BridgeError> {
+ async fn user_info(
+ &self,
+ session: &mut AuthenticatedSession,
+ ) -> Result<String, BridgeError> {
self.maybe_refresh_token(session).await?;
let user_info: CoreUserInfoClaims = self
.client
@@ -225,12 +244,10 @@ impl Authenticator for OauthAuthenticator {
auth_params: HashMap<String, String>,
) -> Result<AuthenticatedSession, BridgeError> {
// 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<S> FromRequestParts<S> for AuthenticatedSession
where
S: Send + Sync,
- Arc<ServerContext>: FromRef<S>
+ Arc<ServerContext>: FromRef<S>,
{
type Rejection = Response;
async fn from_request_parts(
- parts: &mut Parts, state: &S
+ parts: &mut Parts,
+ state: &S,
) -> Result<Self, Self::Rejection> {
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<openidconnect::reqwest::Error<reqwest::Error>>;
+type UserInfoError =
+ openidconnect::UserInfoError<openidconnect::reqwest::Error<reqwest::Error>>;
type RequestTokenError = openidconnect::RequestTokenError<
openidconnect::reqwest::Error<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<dyn Authenticator + Send + Sync> {
- 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<J: Journal<TableUpdate>> Table<J> {
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<J: Journal<TableUpdate>> Table<J> {
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<J: Journal<TableUpdate>> Table<J> {
TableUpdate::Play(card) => {
self.state = self.game()?.clone().play(card)?.into();
Ok(())
- },
+ }
}
}
@@ -292,7 +294,10 @@ pub async fn advance_play<J: Journal<TableUpdate>>(
#[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<Option<UserInfo>, 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<Option<UserInfo>, anyhow::Error> {
}
pub fn use_app_context() -> AppContext {
- let state : AppState = use_context::<AppState>().unwrap();
+ let state: AppState = use_context::<AppState>().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! {
<ContextProvider<AppState> 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<T>(response: Response) -> Result<T, anyhow::Error>
-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?)
}