use serde_json; use diesel::prelude::*; use diesel::sql_types::Text; use diesel::deserialize::FromSql; use diesel::backend::Backend; use crate::schema::history; use crate::{DbConnection, QueryResult, Update}; #[derive(Debug, FromSqlRow)] pub struct UpdateList(Vec); // TODO: decide if updates is really optionnal or not // (like if storing an event without update is usefull ?) /// An event in history #[derive(Debug, Queryable)] pub struct Event { id: i32, player_id: i32, event_date: String, text: String, updates: Option, } impl Event { pub fn name(&self) -> &str { &self.text } /// TODO: why a move here ?? /// Undo all updates in a single transaction pub fn undo(self, conn: &DbConnection) -> QueryResult { conn.transaction(move || { if let Some(ref updates) = self.updates { for update in updates.0.iter() { update.undo(conn, self.player_id)?; } diesel::delete(history::table.find(self.id)).execute(conn)?; } Ok(self) }) } } impl FromSql for UpdateList where String: FromSql, { fn from_sql(bytes: Option<&DB::RawValue>) -> diesel::deserialize::Result { let repr = String::from_sql(bytes)?; Ok(UpdateList(serde_json::from_str::>(&repr)?)) } } #[derive(Debug, Insertable)] #[table_name = "history"] struct NewEvent<'a> { player_id: i32, text: &'a str, updates: Option, } /// Insert a new event /// /// # Warning /// This actually swallow up conversion errors pub fn insert_event(conn: &DbConnection, id: i32, text: &str, updates: &Vec) -> QueryResult { diesel::insert_into(history::table) .values(&NewEvent { player_id: id, text, updates: serde_json::to_string(updates).ok(), }) .execute(conn)?; history::table .order(history::dsl::id.desc()) .first(conn) } pub fn get_last_of_player(conn: &DbConnection, id: i32) -> QueryResult { history::table .filter(history::dsl::player_id.eq(id)) .order(history::dsl::id.desc()) .first(conn) } #[cfg(test)] mod tests { #[test] fn test_insert_event_with_updates() { } }