use diesel::prelude::*; use crate::{ api::EmptyResult, db::{DbConn, schema::favorites}, error::MapResult, }; use super::{CipherId, User, UserId}; #[derive(Identifiable, Queryable, Insertable)] #[diesel(table_name = favorites)] #[diesel(primary_key(user_uuid, cipher_uuid))] pub struct Favorite { pub user_uuid: UserId, pub cipher_uuid: CipherId, } impl Favorite { // Returns whether the specified cipher is a favorite of the specified user. pub async fn is_favorite(cipher_uuid: &CipherId, user_uuid: &UserId, conn: &DbConn) -> bool { conn.run(move |conn| { let query = favorites::table .filter(favorites::cipher_uuid.eq(cipher_uuid)) .filter(favorites::user_uuid.eq(user_uuid)) .count(); query.first::(conn).ok().unwrap_or(0) != 0 }) .await } // Sets whether the specified cipher is a favorite of the specified user. pub async fn set_favorite( favorite: bool, cipher_uuid: &CipherId, user_uuid: &UserId, conn: &DbConn, ) -> EmptyResult { let (old, new) = (Self::is_favorite(cipher_uuid, user_uuid, conn).await, favorite); match (old, new) { (false, true) => { User::update_uuid_revision(user_uuid, conn).await; conn.run(move |conn| { diesel::insert_into(favorites::table) .values((favorites::user_uuid.eq(user_uuid), favorites::cipher_uuid.eq(cipher_uuid))) .execute(conn) .map_res("Error adding favorite") }) .await } (true, false) => { User::update_uuid_revision(user_uuid, conn).await; conn.run(move |conn| { diesel::delete( favorites::table .filter(favorites::user_uuid.eq(user_uuid)) .filter(favorites::cipher_uuid.eq(cipher_uuid)), ) .execute(conn) .map_res("Error removing favorite") }) .await } // Otherwise, the favorite status is already what it should be. _ => Ok(()), } } // Delete all favorite entries associated with the specified cipher. pub async fn delete_all_by_cipher(cipher_uuid: &CipherId, conn: &DbConn) -> EmptyResult { conn.run(move |conn| { diesel::delete(favorites::table.filter(favorites::cipher_uuid.eq(cipher_uuid))) .execute(conn) .map_res("Error removing favorites by cipher") }) .await } // Delete all favorite entries associated with the specified user. pub async fn delete_all_by_user(user_uuid: &UserId, conn: &DbConn) -> EmptyResult { conn.run(move |conn| { diesel::delete(favorites::table.filter(favorites::user_uuid.eq(user_uuid))) .execute(conn) .map_res("Error removing favorites by user") }) .await } /// Return a vec with (cipher_uuid) this will only contain favorite flagged ciphers /// This is used during a full sync so we only need one query for all favorite cipher matches. pub async fn get_all_cipher_uuid_by_user(user_uuid: &UserId, conn: &DbConn) -> Vec { conn.run(move |conn| { favorites::table .filter(favorites::user_uuid.eq(user_uuid)) .select(favorites::cipher_uuid) .load::(conn) .unwrap_or_default() }) .await } }