use crate::schema::users::{users, User}; use crate::schema::{DatabaseError, DbConn}; use chrono::{DateTime, Utc}; use diesel::{Connection, ExpressionMethods, QueryDsl, RunQueryDsl}; use serde::{Deserialize, Serialize}; use uuid::Uuid; table! { gamenight (id) { id -> diesel::sql_types::Uuid, name -> VarChar, datetime -> Timestamptz, owner_id -> Uuid, } } table! { known_games (id) { id -> diesel::sql_types::Uuid, name -> VarChar, } } table! { gamenight_gamelist(gamenight_id, game_id) { gamenight_id -> diesel::sql_types::Uuid, game_id -> diesel::sql_types::Uuid, } } table! { gamenight_participants(gamenight_id, user_id) { gamenight_id -> diesel::sql_types::Uuid, user_id -> diesel::sql_types::Uuid, } } #[derive(Serialize, Deserialize, Debug, Queryable, Clone, Insertable)] #[table_name = "known_games"] pub struct Game { pub id: Uuid, pub name: String, } #[derive(Serialize, Deserialize, Debug, Queryable, Insertable, Clone)] #[table_name = "gamenight"] pub struct Gamenight { pub id: Uuid, pub name: String, pub datetime: DateTime, pub owner_id: Uuid, } #[derive(Serialize, Deserialize, Debug, Queryable, Insertable)] #[table_name = "gamenight_gamelist"] pub struct GamenightGameListEntry { pub gamenight_id: Uuid, pub game_id: Uuid, } #[derive(Serialize, Deserialize, Debug, Queryable, Insertable, Identifiable)] #[table_name = "gamenight_participants"] #[primary_key(gamenight_id, user_id)] pub struct GamenightParticipantsEntry { pub gamenight_id: Uuid, pub user_id: Uuid, } #[derive(Serialize, Deserialize, Debug, Queryable)] pub struct DeleteGamenight { pub game_id: Uuid, } #[derive(Serialize, Deserialize, Debug, Queryable)] pub struct GamenightId { pub gamenight_id: Uuid, } pub async fn get_all_gamenights(conn: &DbConn) -> Result, DatabaseError> { Ok(conn.run(|c| gamenight::table.load::(c)).await?) } pub async fn insert_gamenight( conn: &DbConn, new_gamenight: Gamenight, game_list: Vec, ) -> Result { Ok(conn .run(move |c| { c.transaction::<_, DatabaseError, _>(|| { let id: Uuid = diesel::insert_into(gamenight::table) .values(&new_gamenight) .returning(gamenight::id) .get_result(c)?; let entries: Vec = game_list .iter() .map(|g| GamenightGameListEntry { gamenight_id: new_gamenight.id.clone(), game_id: g.id.clone(), }) .collect(); diesel::insert_into(gamenight_gamelist::table) .values(entries) .execute(c)?; Ok(id) }) }) .await?) } pub async fn get_gamenight(conn: &DbConn, gamenight_id: Uuid) -> Result { Ok(conn .run(move |c| gamenight::table.find(gamenight_id).first(c)) .await?) } pub async fn delete_gamenight(conn: &DbConn, game_id: Uuid) -> Result { Ok(conn .run(move |c| diesel::delete(gamenight::table.filter(gamenight::id.eq(game_id))).execute(c)) .await?) } pub async fn get_all_known_games(conn: &DbConn) -> Result, DatabaseError> { Ok(conn.run(|c| known_games::table.load::(c)).await?) } pub async fn get_games_of_gamenight( conn: &DbConn, gamenight_id: Uuid, ) -> Result, DatabaseError> { Ok(conn .run::<_, Result, _>>(move |c| { let linked_game_ids: Vec = gamenight_gamelist::table .filter(gamenight_gamelist::gamenight_id.eq(gamenight_id)) .load::(c)?; linked_game_ids .iter() .map(|l| { known_games::table .filter(known_games::id.eq(l.game_id)) .first::(c) }) .collect() }) .await?) } pub async fn add_game(conn: &DbConn, game: Game) -> Result { Ok(conn .run(|c| { diesel::insert_into(known_games::table) .values(game) .execute(c) }) .await?) } pub async fn add_unknown_games(conn: &DbConn, games: &mut Vec) -> Result<(), DatabaseError> { let all_games = get_all_known_games(conn).await?; for game in games.iter_mut() { if !all_games.iter().any(|g| g.name == game.name) { game.id = Uuid::new_v4(); add_game(conn, game.clone()).await?; } } Ok(()) } pub async fn load_participants( conn: &DbConn, gamenight_id: Uuid, ) -> Result, DatabaseError> { Ok(conn .run::<_, Result, _>>(move |c| { let linked_participants = gamenight_participants::table .filter(gamenight_participants::gamenight_id.eq(gamenight_id)) .load::(c)?; linked_participants .iter() .map(|l| { users::table .filter(users::id.eq(l.user_id)) .first::(c) }) .collect() }) .await?) } pub async fn add_participant( conn: &DbConn, participant: GamenightParticipantsEntry, ) -> Result { Ok(conn .run(move |c| { diesel::insert_into(gamenight_participants::table) .values(&participant) .on_conflict_do_nothing() .execute(c) }) .await?) } pub async fn remove_participant( conn: &DbConn, participant: GamenightParticipantsEntry, ) -> Result { Ok(conn .run(move |c| diesel::delete(&participant).execute(c)) .await?) }