use lootalot_db::{self as db, DbConnection, QueryResult}; /// Every possible update which can happen during a query #[derive(Serialize, Deserialize, Debug)] pub enum Update { Wealth(db::Wealth), ItemAdded(db::Item), ItemRemoved(db::Item), ClaimAdded(db::Claim), ClaimRemoved(db::Claim), } /// Every value which can be queried #[derive(Serialize, Deserialize, Debug)] pub enum Value { Item(db::Item), Claim(db::Claim), ItemList(Vec), ClaimList(Vec), PlayerList(Vec), } /// A generic response for all queries #[derive(Serialize, Deserialize, Debug, Default)] pub struct ApiResponse { /// The value requested, if any pub value: Option, /// A text to notify user, if relevant pub notification: Option, /// A list of updates, if any pub updates: Option>, /// A text describing errors, if any pub errors: Option, } impl ApiResponse { fn push_update(&mut self, update: Update) { if let Some(v) = self.updates.as_mut() { v.push(update); } else { self.updates = Some(vec![update]); } } fn push_error>(&mut self, error: S) { if let Some(errors) = self.errors.as_mut() { *errors = format!("{}\n{}", errors, error.into()); } else { self.errors = Some(error.into()) } } fn set_value(&mut self, value: Value) { self.value = Some(value); } fn notify>(&mut self, text: S) { self.notification = Some(text.into()); } } pub enum ApiError { DieselError(diesel::result::Error), InvalidAction(String), } /// Every allowed queries on the database pub enum ApiActions { FetchPlayers, FetchInventory, FetchClaims, // Player actions FetchLoot(i32), UpdateWealth(i32, f32), BuyItems(i32, Vec<(i32, Option)>), SellItems(i32, Vec<(i32, Option)>), ClaimItem(i32, i32), UnclaimItem(i32, i32), // Group actions AddLoot(Vec), } pub enum AdminActions { AddPlayer(String, f32), //AddInventoryItem(pub String, pub i32), ResolveClaims, //SetClaimsTimeout(pub i32), } pub fn execute( conn: &DbConnection, query: ApiActions, ) -> Result { let mut response = ApiResponse::default(); match query { ApiActions::FetchPlayers => { response.set_value(Value::PlayerList(db::Players(conn).all()?)); } ApiActions::FetchInventory => { response.set_value(Value::ItemList(db::Inventory(conn).all()?)); } ApiActions::FetchClaims => { response.set_value(Value::ClaimList(db::fetch_claims(conn)?)); } ApiActions::FetchLoot(id) => { response.set_value(Value::ItemList(db::LootManager(conn, id).all()?)); } ApiActions::UpdateWealth(id, amount) => { response.push_update(Update::Wealth( db::AsPlayer(conn, id).update_wealth(amount)?, )); } ApiActions::BuyItems(id, params) => { let mut cumulated_diff: Vec = Vec::with_capacity(params.len()); let mut added_items: u16 = 0; for (item_id, price_mod) in params.into_iter() { // Use a transaction to avoid incoherant state in case of error if let Ok((item, diff)) = db::buy_item_from_inventory(conn, id, item_id, price_mod) { cumulated_diff.push(diff); response.push_update(Update::ItemAdded(item)); added_items += 1; } else { response.push_error(format!("Error adding {}", item_id)); } } response.notify(format!("Added {} items", added_items)); response.push_update(Update::Wealth( cumulated_diff .into_iter() .fold(db::Wealth::from_gp(0.0), |acc, i| acc + i), )); } ApiActions::SellItems(id, params) => { let mut all_results: Vec = Vec::with_capacity(params.len()); let mut sold_items: u16 = 0; for (loot_id, price_mod) in params.into_iter() { if let Ok((deleted, diff)) = db::sell_item_transaction(conn, id, loot_id, price_mod) { all_results.push(diff); response.push_update(Update::ItemRemoved(deleted)); sold_items += 1; } else { response.push_error(format!("Error selling {}", loot_id)); } } response.notify(format!("Sold {} items", sold_items)); response.push_update(Update::Wealth( all_results .into_iter() .fold(db::Wealth::from_gp(0.0), |acc, i| acc + i), )); } ApiActions::ClaimItem(id, item) => { response.push_update(Update::ClaimAdded( db::Claims(conn).add(id, item)?, )); } ApiActions::UnclaimItem(id, item) => { response.push_update(Update::ClaimRemoved( db::Claims(conn).remove(id, item)?, )); } // Group actions ApiActions::AddLoot(items) => {} } Ok(response) }