From ee0b7b2b7a7d4d9894d5ca20dd00fa6fbcb1c210 Mon Sep 17 00:00:00 2001 From: Artus Date: Tue, 29 Oct 2019 16:19:00 +0100 Subject: [PATCH] adds UpdateResult class of queries --- lootalot_db/src/lib.rs | 85 +++++++++++++++------------- lootalot_db/src/models/item.rs | 19 +++---- lootalot_db/src/models/player/mod.rs | 11 ++-- src/api.rs | 8 +-- 4 files changed, 64 insertions(+), 59 deletions(-) diff --git a/lootalot_db/src/lib.rs b/lootalot_db/src/lib.rs index 3f420d0..3b917b7 100644 --- a/lootalot_db/src/lib.rs +++ b/lootalot_db/src/lib.rs @@ -18,9 +18,9 @@ mod schema; pub use models::{ claim::{Claim, Claims}, - item::{Item, LootManager, Inventory}, - player::{Player, Wealth, Players, AsPlayer}, history::{Event, UpdateList}, + item::{Inventory, Item, LootManager}, + player::{AsPlayer, Player, Players, Wealth}, }; /// The connection used @@ -29,6 +29,7 @@ pub type DbConnection = SqliteConnection; pub type Pool = r2d2::Pool>; /// The result of a query on DB pub type QueryResult = Result; +pub type UpdateResult = QueryResult; /// Sets up a connection pool and returns it. /// Uses the DATABASE_URL environment variable (must be set) @@ -41,7 +42,6 @@ pub fn create_pool() -> Pool { .expect("Failed to create pool.") } - /// Every possible update which can happen during a query #[derive(Serialize, Deserialize, Debug)] pub enum Update { @@ -54,24 +54,25 @@ pub enum Update { impl Update { /// Change back what has been updated - fn undo(&self, conn: &DbConnection, id: i32) -> QueryResult { + fn undo(&self, conn: &DbConnection, id: i32) -> UpdateResult { Ok(match self { - Update::Wealth(diff) => { - Update::Wealth(AsPlayer(conn, id).update_wealth(-diff.to_gp())?) - }, - Update::ItemAdded(item) => { - Update::ItemRemoved(LootManager(conn, id).remove(item.id)?) - }, - Update::ItemRemoved(item) => { - Update::ItemAdded(LootManager(conn, id).add_from(&item)?) - }, + Update::Wealth(diff) => AsPlayer(conn, id).update_wealth(-diff.to_gp())?, + Update::ItemAdded(item) => LootManager(conn, id).find(item.id)?.remove(conn)?, + Update::ItemRemoved(item) => LootManager(conn, id).add_from(&item)?, // Unused for now - Update::ClaimAdded(claim) => { Update::ClaimRemoved(*claim) }, - Update::ClaimRemoved(claim) => { Update::ClaimAdded(*claim) }, + Update::ClaimAdded(claim) => Update::ClaimRemoved(*claim), + Update::ClaimRemoved(claim) => Update::ClaimAdded(*claim), }) } } +/// TODO: use this to wrap update in UpdateResult, allowing unified interface +/// whether a query makes multiple updates or just one. +enum OneOrMore { + One(Update), + More(Vec), +} + /// Every value which can be queried #[derive(Debug)] pub enum Value { @@ -107,17 +108,19 @@ pub fn sell_item_transaction( id: i32, loot_id: i32, price_mod: Option, -) -> QueryResult<(Item, Wealth)> { +) -> QueryResult<(Update, Wealth)> { conn.transaction(|| { - let deleted = LootManager(conn, id) - .remove(loot_id)?; - let mut sell_value = deleted.sell_value() as f64; + let to_delete = LootManager(conn, id).find(loot_id)?; + let mut sell_value = to_delete.sell_value() as f64; if let Some(modifier) = price_mod { sell_value *= modifier; } - let wealth = AsPlayer(conn, id) - .update_wealth(sell_value)?; - Ok((deleted, wealth)) + let deleted = to_delete.remove(conn)?; + if let Update::Wealth(wealth) = AsPlayer(conn, id).update_wealth(sell_value)? { + Ok((deleted, wealth)) + } else { + Err(diesel::result::Error::RollbackTransaction) + } }) } @@ -131,7 +134,7 @@ pub fn buy_item_from_inventory( id: i32, item_id: i32, price_mod: Option, -) -> QueryResult<(Item, Wealth)> { +) -> QueryResult<(Update, Wealth)> { conn.transaction(|| { // Find item in inventory let item = Inventory(conn).find(item_id)?; @@ -140,9 +143,11 @@ pub fn buy_item_from_inventory( Some(modifier) => item.value() as f64 * modifier, None => item.value() as f64, }; - AsPlayer(conn, id) - .update_wealth(-sell_price) - .map(|diff| (new_item, diff)) + if let Update::Wealth(diff) = AsPlayer(conn, id).update_wealth(-sell_price)? { + Ok((new_item, diff)) + } else { + Err(diesel::result::Error::RollbackTransaction) + } }) } @@ -161,35 +166,39 @@ pub fn resolve_claims(conn: &DbConnection) -> QueryResult<()> { for (item, mut claims) in data { if claims.len() > 1 { // TODO: better sorting mechanism :) - claims.sort_by(|a,b| a.resolve.cmp(&b.resolve)); + claims.sort_by(|a, b| a.resolve.cmp(&b.resolve)); } let winner = claims.get(0).expect("Claims should not be empty !"); let player_id = winner.player_id; - winner.resolve_claim(conn)?; - models::player::AsPlayer(conn, player_id) - .update_debt(item.sell_value())?; + winner.resolve_claim(conn)?; + models::player::AsPlayer(conn, player_id).update_debt(item.sell_value())?; } Ok(()) }) } /// Split up and share group money among selected players -pub fn split_and_share(conn: &DbConnection, amount: i32, players: Vec) -> QueryResult { +pub fn split_and_share( + conn: &DbConnection, + amount: i32, + players: &Vec, +) -> QueryResult { let share = ( - amount / (players.len() + 1) as i32 // +1 share for the group + amount / (players.len() + 1) as i32 + // +1 share for the group ) as f64; conn.transaction(|| { - for p in players.into_iter() { - let player = Players(conn).find(p)?; + for p in players { + let player = Players(conn).find(*p)?; // Take debt into account match share - player.debt as f64 { rest if rest > 0.0 => { - AsPlayer(conn, p).update_debt(-player.debt)?; - AsPlayer(conn, p).update_wealth(rest)?; + AsPlayer(conn, *p).update_debt(-player.debt)?; + AsPlayer(conn, *p).update_wealth(rest)?; AsPlayer(conn, 0).update_wealth(-rest)?; - }, + } _ => { - AsPlayer(conn, p).update_debt(-share as i32)?; + AsPlayer(conn, *p).update_debt(-share as i32)?; } } } diff --git a/lootalot_db/src/models/item.rs b/lootalot_db/src/models/item.rs index 49e2290..171bb02 100644 --- a/lootalot_db/src/models/item.rs +++ b/lootalot_db/src/models/item.rs @@ -3,7 +3,7 @@ use diesel::expression::exists::Exists; use diesel::prelude::*; use crate::schema::{items, looted}; -use crate::{DbConnection, QueryResult}; +use crate::{DbConnection, QueryResult, Update, UpdateResult }; type ItemColumns = (looted::id, looted::name, looted::base_price); const ITEM_COLUMNS: ItemColumns = (looted::id, looted::name, looted::base_price); type OwnedBy = Select; @@ -27,6 +27,11 @@ impl Item { self.base_price / 2 } + pub fn remove(self, conn: &DbConnection) -> UpdateResult { + diesel::delete(looted::table.find(self.id)).execute(conn)?; + Ok(Update::ItemRemoved(self)) + } + fn owned_by(player: i32) -> OwnedBy { Loot::owned_by(player).select(ITEM_COLUMNS) } @@ -124,7 +129,7 @@ impl<'q> LootManager<'q> { .first(self.0)?) } - pub(crate) fn add>(self, name: S, base_price: i32) -> QueryResult { + pub(crate) fn add>(self, name: S, base_price: i32) -> UpdateResult { self.add_from(&Item { id: 0, name: name.into(), @@ -133,7 +138,7 @@ impl<'q> LootManager<'q> { } /// Adds a copy of the given item inside player chest - pub fn add_from(self, item: &Item) -> QueryResult { + pub fn add_from(self, item: &Item) -> UpdateResult { let new_item = NewLoot { name: &item.name, base_price: item.base_price, @@ -142,13 +147,7 @@ impl<'q> LootManager<'q> { diesel::insert_into(looted::table) .values(&new_item) .execute(self.0)?; - self.last() - } - - pub fn remove(self, item_id: i32) -> QueryResult { - let deleted = self.find(item_id)?; - diesel::delete(looted::table.find(deleted.id)).execute(self.0)?; - Ok(deleted) + Ok(Update::ItemAdded(self.last()?)) } } diff --git a/lootalot_db/src/models/player/mod.rs b/lootalot_db/src/models/player/mod.rs index 820dbcd..20ebc6f 100644 --- a/lootalot_db/src/models/player/mod.rs +++ b/lootalot_db/src/models/player/mod.rs @@ -1,5 +1,5 @@ use crate::schema::players; -use crate::{DbConnection, QueryResult}; +use crate::{DbConnection, QueryResult, Update, UpdateResult}; use diesel::prelude::*; mod notification; @@ -50,10 +50,7 @@ impl<'q> Players<'q> { /// Notify all players of an event pub fn notifiy_all(&self, text: &str) -> QueryResult<()> { - for id in self.all()? - .into_iter() - .map(|p| p.id) - { + for id in self.all()?.into_iter().map(|p| p.id) { self.notify(id, text); } Ok(()) @@ -75,7 +72,7 @@ impl<'q> AsPlayer<'q> { notification::pop_all_for(self.1, self.0) } /// Updates this player's wealth, returning the difference - pub fn update_wealth(&self, value_in_gp: f64) -> QueryResult { + pub fn update_wealth(&self, value_in_gp: f64) -> UpdateResult { use crate::schema::players::dsl::*; let current_wealth = players .find(self.1) @@ -86,7 +83,7 @@ impl<'q> AsPlayer<'q> { .filter(id.eq(self.1)) .set(&updated_wealth) .execute(self.0)?; - Ok(updated_wealth - current_wealth) + Ok(Update::Wealth(updated_wealth - current_wealth)) } /// Updates this player's debt diff --git a/src/api.rs b/src/api.rs index 4eb2bca..95b811d 100644 --- a/src/api.rs +++ b/src/api.rs @@ -113,9 +113,7 @@ pub fn execute( None } ApiActions::UpdateWealth(id, amount) => { - response.push_update(Update::Wealth( - db::AsPlayer(conn, id).update_wealth(amount)?, - )); + response.push_update(db::AsPlayer(conn, id).update_wealth(amount)?); response.notify(format!("Mis à jour ({}po)!", amount)); Some((id, "Argent mis à jour")) } @@ -165,15 +163,17 @@ pub fn execute( .fold(db::Wealth::from_gp(0.0), |acc, i| acc + i); match id { 0 => { + let players = params.players.expect("The player list should be passed in !"); let share = db::split_and_share( conn, total_amount.to_gp() as i32, - params.players.expect("Should not be None"), + &players, )?; response.notify(format!( "Les objets ont été vendus, chaque joueur a reçu {} po", share.to_gp() )); + //response.push_update(Update::GroupShare(players, share)); response.push_update(Update::Wealth(share)); } _ => {