use crate::{DbConnection, QueryResult}; use diesel::prelude::*; use crate::models::{self, item::Loot}; use crate::schema::claims; /// A Claim is a request by a single player on an item from group chest. #[derive(Identifiable, Queryable, Associations, Serialize, Deserialize, Debug)] #[belongs_to(Loot)] pub struct Claim { /// DB Identifier pub id: i32, /// ID that references the player making this claim pub player_id: i32, /// ID that references the loot claimed pub loot_id: i32, /// WIP: How bad the player wants this item pub resolve: i32, } impl Claim { /// Resolves this claim (player wins the item) and deletes it pub fn resolve_claim(&self, conn: &DbConnection) -> QueryResult<()> { let loot: Loot = Loot::find(self.loot_id).first(conn)?; loot.set_owner(self.player_id, conn)?; self.remove(conn)?; Ok(()) } fn remove(&self, conn: &DbConnection) -> QueryResult<()> { diesel::delete(claims::table.find(self.id)).execute(conn)?; Ok(()) } } pub struct Claims<'q>(pub &'q DbConnection); impl<'q> Claims<'q> { /// Get all claims from database pub fn all(&self) -> QueryResult> { claims::table.load(self.0) } /// Finds a single claim by association of player and loot ids. pub fn find(&self, player_id: i32, loot_id: i32) -> QueryResult { claims::table .filter(claims::dsl::player_id.eq(player_id)) .filter(claims::dsl::loot_id.eq(loot_id)) .first(self.0) } /// Adds a claim in database and returns it. /// /// Will validate that the claimed item exists and is /// actually owned by the group. /// Duplicates are also ignored. pub fn add(self, player_id: i32, loot_id: i32) -> QueryResult { // We need to validate that the claimed item exists // AND is actually owned by group (id 0) let _item = models::item::LootManager(self.0, 0).find(loot_id)?; // We also check if claims does not already exists if let Ok(_) = self.find(player_id, loot_id) { return Err(diesel::result::Error::RollbackTransaction); } let claim = NewClaim::new(player_id, loot_id); diesel::insert_into(claims::table) .values(&claim) .execute(self.0)?; // Return the created claim claims::table .order(claims::dsl::id.desc()) .first::(self.0) } /// Removes a claim from database, returning it pub fn remove(self, player_id: i32, loot_id: i32) -> QueryResult { let claim = self.find(player_id, loot_id)?; claim.remove(self.0)?; Ok(claim) } pub fn filtered_by_loot(&self, loot_id: i32) -> QueryResult> { claims::table .filter(claims::dsl::loot_id.eq(loot_id)) .load(self.0) } pub(crate) fn grouped_by_item(&self) -> QueryResult)>> { let group_loot: Vec = Loot::owned_by(0).load(self.0)?; let claims = claims::table.load(self.0)?.grouped_by(&group_loot); Ok(group_loot .into_iter() .map(|loot| loot.into_item()) .zip(claims) .collect::>()) } } #[derive(Insertable, Debug)] #[table_name = "claims"] struct NewClaim { player_id: i32, loot_id: i32, } impl NewClaim { fn new(player_id: i32, loot_id: i32) -> Self { Self { player_id, loot_id } } } #[cfg(test)] mod tests { use super::*; type TestResult = Result<(), diesel::result::Error>; fn test_connection() -> Result { let conn = DbConnection::establish(":memory:").map_err(|_| diesel::result::Error::NotFound)?; diesel_migrations::run_pending_migrations(&conn) .map_err(|_| diesel::result::Error::NotFound)?; let manager = models::player::Players(&conn); manager.add("Player1", 0.0)?; manager.add("Player2", 0.0)?; crate::LootManager(&conn, 0).add_from(&crate::Item { id: 0, name: "Epee".to_string(), base_price: 30, })?; crate::LootManager(&conn, 1).add_from(&crate::Item { id: 0, name: "Arc".to_string(), base_price: 20, })?; Ok(conn) } #[test] fn add_claim() -> TestResult { let conn = test_connection()?; Claims(&conn).add(1, 1)?; assert_eq!(Claims(&conn).all()?.len(), 1); Ok(()) } #[test] fn cannot_duplicate_by_adding() -> TestResult { let conn = test_connection()?; Claims(&conn).add(1, 1)?; let res = Claims(&conn).add(1, 1); assert_eq!(res.is_err(), true); assert_eq!(Claims(&conn).all()?.len(), 1); Ok(()) } #[test] fn remove_claim() -> TestResult { let conn = test_connection()?; let claim = Claims(&conn).add(1, 1)?; claim.remove(&conn); assert_eq!(Claims(&conn).all()?.len(), 0); Ok(()) } #[test] fn cannot_only_claim_from_group() -> TestResult { let conn = test_connection()?; let claim = Claims(&conn).add(1, 2); assert_eq!(claim.is_err(), true); assert_eq!(Claims(&conn).all()?.len(), 0); Ok(()) } }