use anyhow::{ Error, Result, }; use chrono::naive::NaiveDateTime; use diesel::{ prelude::*, Identifiable, Insertable, Queryable, }; use diesel_async::{ AsyncPgConnection, RunQueryDsl, }; use sha1::Digest; use crate::db::schema::*; #[derive(Queryable, Identifiable, PartialEq, Debug, Clone)] #[diesel(table_name = memes)] pub struct Meme { pub id: i32, pub guild: i64, pub title: String, pub content: Option, pub image_id: Option, pub audio_id: Option, pub metadata_id: i32, } impl Meme { pub async fn image(&self, conn: &mut AsyncPgConnection) -> Option> { match self.image_id { Some(x) => { Some(images::table.filter(images::id.eq(x)).first(conn).await.map_err(Error::from)) }, None => None, } } pub async fn audio(&self, conn: &mut AsyncPgConnection) -> Option> { let Some(x) = self.audio_id else { return None; }; Some(audio::table.filter(audio::id.eq(x)).first(conn).await.map_err(Error::from)) } pub async fn find(conn: &mut AsyncPgConnection, id: i32) -> Result { memes::table.find(id).get_result(conn).await.map_err(Error::from) } } #[derive(Insertable, PartialEq, Debug, Clone)] #[diesel(table_name = memes)] pub struct NewMeme { pub title: String, pub guild: i64, pub content: Option, pub image_id: Option, pub audio_id: Option, pub metadata_id: i32, } impl NewMeme { pub async fn save(mut self, conn: &mut AsyncPgConnection, by_user: u64) -> Result { let metadata = Metadata::create(conn, by_user).await?; self.metadata_id = metadata.id; diesel::insert_into(memes::table) .values(&self) .get_result::(conn) .await .map_err(Error::from) } } #[derive(Queryable, Identifiable, PartialEq, Debug)] #[diesel(table_name = audio)] pub struct Audio { pub id: i32, pub data: Vec, pub metadata_id: i32, pub data_hash: Vec, } impl Audio { pub async fn create(conn: &mut AsyncPgConnection, data: Vec, by_user: u64) -> Result { let mut data_hash = ::sha1::Sha1::new(); data_hash.update(&data); let data_hash = data_hash.finalize().to_vec(); let id = audio::table .select(audio::id) .filter(audio::data_hash.eq(&data_hash)) .get_results::(conn) .await?; if let Some(id) = <[_]>::first(&id) { return Ok(*id); } let metadata = Metadata::create(conn, by_user).await?; let new_audio = NewAudio { data, data_hash, metadata_id: metadata.id, }; diesel::insert_into(audio::table) .values(&new_audio) .returning(audio::id) .get_result(conn) .await .map_err(Error::from) } } #[derive(Insertable, PartialEq, Debug)] #[diesel(table_name = audio)] pub struct NewAudio { pub data: Vec, pub metadata_id: i32, pub data_hash: Vec, } #[derive(Queryable, Identifiable, PartialEq, Debug)] #[diesel(table_name = images)] pub struct Image { pub id: i32, pub data: Vec, pub metadata_id: i32, pub data_hash: Vec, pub filename: String, } impl Image { pub async fn create( conn: &mut AsyncPgConnection, filename: &str, data: Vec, by_user: u64, ) -> Result { let mut data_hash = ::sha1::Sha1::new(); data_hash.update(&data); let data_hash = data_hash.finalize().to_vec(); let id = images::table .select(images::id) .filter(images::data_hash.eq(&data_hash)) .get_results::(conn) .await?; if let Some(id) = <[_]>::first(&id) { return Ok(*id); } let metadata = Metadata::create(conn, by_user).await?; let new_image = NewImage { data, data_hash, filename: filename.to_owned(), metadata_id: metadata.id, }; diesel::insert_into(images::table) .values(&new_image) .returning(images::id) .get_result(conn) .await .map_err(Error::from) } } #[derive(Insertable, PartialEq, Debug)] #[diesel(table_name = images)] pub struct NewImage { pub data: Vec, pub metadata_id: i32, pub data_hash: Vec, pub filename: String, } #[derive(Queryable, Identifiable, PartialEq, Debug, Clone)] #[diesel(table_name = metadata)] pub struct Metadata { pub id: i32, pub created: NaiveDateTime, pub created_by: i64, } impl Metadata { pub async fn create(conn: &mut AsyncPgConnection, by_user: u64) -> Result { diesel::insert_into(metadata::table) .values(&NewMetadata { created_by: by_user as i64, }) .get_result::(conn) .await .map_err(Error::from) } pub async fn find(conn: &mut AsyncPgConnection, id: i32) -> Result { metadata::table.find(id).get_result::(conn).await.map_err(Error::from) } } #[derive(Insertable, PartialEq, Debug)] #[diesel(table_name = metadata)] pub struct NewMetadata { pub created_by: i64, } #[derive(Queryable, Identifiable, PartialEq, Debug)] #[diesel(table_name = audit_records)] pub struct AuditRecord { pub id: i32, pub updated: NaiveDateTime, pub updated_by: i64, pub metadata_id: i32, } impl AuditRecord { pub async fn create( conn: &mut AsyncPgConnection, metadata: i32, by_user: u64, ) -> Result { diesel::insert_into(audit_records::table) .values(&NewAuditRecord { updated_by: by_user as i64, metadata_id: metadata, }) .get_result::(conn) .await .map_err(Error::from) } } #[derive(Insertable, PartialEq, Debug)] #[diesel(table_name = audit_records)] pub struct NewAuditRecord { pub updated_by: i64, pub metadata_id: i32, } #[derive(Queryable, Identifiable, PartialEq, Debug)] #[diesel(table_name = tombstones)] pub struct Tombstone { pub id: i32, pub guild: i64, pub deleted: NaiveDateTime, pub deleted_by: i64, pub metadata_id: i32, pub meme_id: i32, } #[derive(Insertable, PartialEq, Debug)] #[diesel(table_name = tombstones)] pub struct NewTombstone { pub deleted_by: i64, pub guild: i64, pub metadata_id: i32, pub meme_id: i32, } #[derive(Queryable, Identifiable, PartialEq, Debug)] #[diesel(table_name = invocation_records)] pub struct InvocationRecord { pub id: i32, pub user_id: i64, pub guild: i64, pub message_id: i64, pub meme_id: i32, pub time: NaiveDateTime, pub random: bool, } #[derive(Insertable, PartialEq, Debug)] #[diesel(table_name = invocation_records)] pub struct NewInvocationRecord { pub user_id: i64, pub guild: i64, pub message_id: i64, pub meme_id: i32, pub random: bool, } impl InvocationRecord { pub async fn create( conn: &mut AsyncPgConnection, user_id: u64, guild_id: u64, message_id: u64, meme_id: i32, random: bool, ) -> Result { diesel::insert_into(invocation_records::table) .values(&NewInvocationRecord { user_id: user_id as i64, message_id: message_id as i64, guild: guild_id as i64, meme_id, random, }) .get_result::(conn) .await .map_err(Error::from) } pub async fn last(conn: &mut AsyncPgConnection, guild_id: u64) -> Result { invocation_records::table .filter(invocation_records::guild.eq(guild_id as i64)) .order(invocation_records::time.desc()) .first(conn) .await .map_err(Error::from) } pub async fn last_n( conn: &mut AsyncPgConnection, n: usize, guild_id: u64, ) -> Result> { invocation_records::table .filter(invocation_records::guild.eq(guild_id as i64)) .order(invocation_records::time.desc()) .limit(n as i64) .load(conn) .await .map_err(Error::from) } }