diff options
Diffstat (limited to 'src/db')
| -rw-r--r-- | src/db/mod.rs | 257 | ||||
| -rw-r--r-- | src/db/models.rs | 471 |
2 files changed, 366 insertions, 362 deletions
diff --git a/src/db/mod.rs b/src/db/mod.rs index 722c72d..1ff05ce 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -1,128 +1,129 @@ -use std::{
- env,
- convert::AsRef,
-};
-
-use diesel::{
- prelude::*,
- r2d2::{ConnectionManager, ManageConnection},
- NotFound,
-};
-
-use super::{Result, Error};
-pub use self::models::*;
-use self::schema::*;
-
-mod schema;
-mod models;
-
-lazy_static! {
- static ref DB_URL: String = env::var("DATABASE_URL").expect("no database url in environment").into();
- static ref CONN_MGR: ConnectionManager<PgConnection> = ConnectionManager::new(DB_URL.clone());
-}
-
-pub fn connection() -> Result<PgConnection> {
- CONN_MGR.connect().map_err(Error::from)
-}
-
-pub fn find_meme<T: AsRef<str>>(conn: &PgConnection, search: T) -> Result<Meme> {
- use diesel::dsl::sql;
- use diesel::sql_types::Text;
-
- let search = search.as_ref();
-
- // TODO: check for injection
- let mut meme = memes::table
- .filter(memes::title.eq(search))
- .limit(1)
- .first::<Meme>(conn);
-
- if let Err(NotFound) = meme {
- let format_search = format!("%{}%", search);
-
- meme = memes::table
- .filter(memes::title.ilike(&format_search).or(sql("content ILIKE ").bind::<Text, _>(&format_search)))
- .limit(1)
- .first::<Meme>(conn);
- }
-
- meme
- .map_err(Error::from)
-}
-
-pub fn delete_meme<T: AsRef<str>>(conn: &PgConnection, search: T, deleted_by: u64) -> Result<()> {
- conn.transaction::<(), Error, _>(|| {
- let deleted = memes::table
- .filter(memes::title.eq(search.as_ref()))
- .first::<Meme>(conn)?;
-
- ::diesel::delete(memes::table)
- .filter(memes::id.eq(deleted.id))
- .execute(conn)?;
-
- if let Some(image_id) = deleted.image_id {
- let count = memes::table
- .filter(memes::image_id.eq(image_id))
- .count()
- .execute(conn)?;
-
- if count == 0 {
- ::diesel::delete(images::table)
- .filter(images::id.eq(image_id))
- .execute(conn)?;
- }
- }
-
- if let Some(audio_id) = deleted.audio_id {
- let count = memes::table
- .select(::diesel::dsl::count_star())
- .filter(memes::audio_id.eq(audio_id))
- .execute(conn)?;
-
- if count == 0 {
- ::diesel::delete(audio::table)
- .filter(audio::id.eq(audio_id))
- .execute(conn)?;
- }
- }
-
- let tombstone = NewTombstone {
- deleted_by: deleted_by as i64,
- metadata_id: deleted.metadata_id,
- meme_id: deleted.id,
- };
-
- let _ = ::diesel::insert_into(tombstones::table)
- .values(&tombstone)
- .execute(conn)?;
-
- Ok(())
- })
-}
-
-pub fn rand_text(conn: &PgConnection) -> Result<Meme> {
- memes::table
- .filter(memes::content.is_not_null())
- .order(random.desc())
- .first::<Meme>(conn)
- .map_err(Error::from)
-}
-
-pub fn rand_image(conn: &PgConnection) -> Result<Meme> {
- memes::table
- .filter(memes::image_id.is_not_null())
- .order(random.desc())
- .first::<Meme>(conn)
- .map_err(Error::from)
-}
-
-pub fn rand_audio(conn: &PgConnection) -> Result<Meme> {
- memes::table
- .filter(memes::audio_id.is_not_null())
- .order(random.desc())
- .first::<Meme>(conn)
- .map_err(Error::from)
-}
-
-use diesel::sql_types;
-no_arg_sql_function!(random, sql_types::Double, "SQL random() function");
+use std::{ + convert::AsRef, + env, +}; + +use diesel::{ + NotFound, + prelude::*, + r2d2::{ConnectionManager, ManageConnection}, +}; +use diesel::sql_types; + +use crate::{Error, Result}; + +pub use self::models::*; +use self::schema::*; + +mod schema; +mod models; + +lazy_static! { + static ref DB_URL: String = env::var("DATABASE_URL").expect("no database url in environment").into(); + static ref CONN_MGR: ConnectionManager<PgConnection> = ConnectionManager::new(DB_URL.clone()); +} + +pub fn connection() -> Result<PgConnection> { + CONN_MGR.connect().map_err(Error::from) +} + +pub fn find_meme<T: AsRef<str>>(conn: &PgConnection, search: T) -> Result<Meme> { + use diesel::dsl::sql; + use diesel::sql_types::Text; + + let search = search.as_ref(); + + // TODO: check for injection + let mut meme = memes::table + .filter(memes::title.eq(search)) + .limit(1) + .first::<Meme>(conn); + + if let Err(NotFound) = meme { + let format_search = format!("%{}%", search); + + meme = memes::table + .filter(memes::title.ilike(&format_search).or(sql("content ILIKE ").bind::<Text, _>(&format_search))) + .limit(1) + .first::<Meme>(conn); + } + + meme + .map_err(Error::from) +} + +pub fn delete_meme<T: AsRef<str>>(conn: &PgConnection, search: T, deleted_by: u64) -> Result<()> { + conn.transaction::<(), Error, _>(|| { + let deleted = memes::table + .filter(memes::title.eq(search.as_ref())) + .first::<Meme>(conn)?; + + ::diesel::delete(memes::table) + .filter(memes::id.eq(deleted.id)) + .execute(conn)?; + + if let Some(image_id) = deleted.image_id { + let count = memes::table + .filter(memes::image_id.eq(image_id)) + .count() + .execute(conn)?; + + if count == 0 { + ::diesel::delete(images::table) + .filter(images::id.eq(image_id)) + .execute(conn)?; + } + } + + if let Some(audio_id) = deleted.audio_id { + let count = memes::table + .select(::diesel::dsl::count_star()) + .filter(memes::audio_id.eq(audio_id)) + .execute(conn)?; + + if count == 0 { + ::diesel::delete(audio::table) + .filter(audio::id.eq(audio_id)) + .execute(conn)?; + } + } + + let tombstone = NewTombstone { + deleted_by: deleted_by as i64, + metadata_id: deleted.metadata_id, + meme_id: deleted.id, + }; + + let _ = ::diesel::insert_into(tombstones::table) + .values(&tombstone) + .execute(conn)?; + + Ok(()) + }) +} + +pub fn rand_text(conn: &PgConnection) -> Result<Meme> { + memes::table + .filter(memes::content.is_not_null()) + .order(random.desc()) + .first::<Meme>(conn) + .map_err(Error::from) +} + +pub fn rand_image(conn: &PgConnection) -> Result<Meme> { + memes::table + .filter(memes::image_id.is_not_null()) + .order(random.desc()) + .first::<Meme>(conn) + .map_err(Error::from) +} + +pub fn rand_audio(conn: &PgConnection) -> Result<Meme> { + memes::table + .filter(memes::audio_id.is_not_null()) + .order(random.desc()) + .first::<Meme>(conn) + .map_err(Error::from) +} + +no_arg_sql_function!(random, sql_types::Double, "SQL random() function"); diff --git a/src/db/models.rs b/src/db/models.rs index 659a38a..67aa1ca 100644 --- a/src/db/models.rs +++ b/src/db/models.rs @@ -1,234 +1,237 @@ -use chrono::naive::NaiveDateTime;
-use diesel::prelude::*;
-
-use super::schema::*;
-use crate::{Result, Error};
-
-#[derive(Queryable, Identifiable, PartialEq, Debug)]
-#[table_name="memes"]
-pub struct Meme {
- pub id: i32,
- pub title: String,
- pub content: Option<String>,
- pub image_id: Option<i32>,
- pub audio_id: Option<i32>,
- pub metadata_id: i32,
-}
-
-impl Meme {
- pub fn image(&self, conn: &PgConnection) -> Option<Result<Image>> {
- self.image_id.map(|x: i32| images::table.filter(images::id.eq(x)).first(conn).map_err(Error::from))
- }
-
- pub fn audio(&self, conn: &PgConnection) -> Option<Result<Audio>> {
- self.audio_id.map(|x: i32| audio::table.filter(audio::id.eq(x)).first(conn).map_err(Error::from))
- }
-
- pub fn find(conn: &PgConnection, id: i32) -> Result<Meme> {
- memes::table.find(id).get_result(conn).map_err(Error::from)
- }
-}
-
-#[derive(Insertable, PartialEq, Debug)]
-#[table_name="memes"]
-pub struct NewMeme {
- pub title: String,
- pub content: Option<String>,
- pub image_id: Option<i32>,
- pub audio_id: Option<i32>,
- pub metadata_id: i32,
-}
-
-impl NewMeme {
- pub fn save(mut self, conn: &PgConnection, by_user: u64) -> Result<Meme> {
- let metadata = Metadata::create(conn, by_user)?;
-
- self.metadata_id = metadata.id;
-
- ::diesel::insert_into(memes::table)
- .values(&self)
- .get_result::<Meme>(conn)
- .map_err(Error::from)
- }
-}
-
-
-#[derive(Queryable, Identifiable, PartialEq, Debug)]
-#[table_name="audio"]
-pub struct Audio {
- pub id: i32,
- pub data: Vec<u8>,
- pub metadata_id: i32,
- pub data_hash: Vec<u8>,
-}
-
-impl Audio {
- pub fn create(conn: &PgConnection, data: Vec<u8>, by_user: u64) -> Result<i32> {
- let mut data_hash = ::sha1::Sha1::new();
- data_hash.update(&data);
- let data_hash = data_hash.digest().bytes().to_vec();
-
- let id = audio::table
- .select(audio::id)
- .filter(audio::data_hash.eq(&data_hash))
- .get_results::<i32>(conn)?;
-
- if let Some(id) = id.first() {
- return Ok(*id);
- }
-
- let metadata = Metadata::create(conn, by_user)?;
-
- 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)
- .map_err(Error::from)
- }
-}
-
-#[derive(Insertable, PartialEq, Debug)]
-#[table_name="audio"]
-pub struct NewAudio {
- pub data: Vec<u8>,
- pub metadata_id: i32,
- pub data_hash: Vec<u8>,
-}
-
-
-#[derive(Queryable, Identifiable, PartialEq, Debug)]
-#[table_name="images"]
-pub struct Image {
- pub id: i32,
- pub data: Vec<u8>,
- pub metadata_id: i32,
- pub data_hash: Vec<u8>,
- pub filename: String,
-}
-
-impl Image {
- pub fn create(conn: &PgConnection, filename: &str, data: Vec<u8>, by_user: u64) -> Result<i32> {
- let mut data_hash = ::sha1::Sha1::new();
- data_hash.update(&data);
- let data_hash = data_hash.digest().bytes().to_vec();
-
- let id = images::table
- .select(images::id)
- .filter(images::data_hash.eq(&data_hash))
- .get_results::<i32>(conn)?;
-
- if let Some(id) = id.first() {
- return Ok(*id);
- }
-
- let metadata = Metadata::create(conn, by_user)?;
-
- 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)
- .map_err(Error::from)
- }
-}
-
-#[derive(Insertable, PartialEq, Debug)]
-#[table_name="images"]
-pub struct NewImage {
- pub data: Vec<u8>,
- pub metadata_id: i32,
- pub data_hash: Vec<u8>,
- pub filename: String,
-}
-
-
-#[derive(Queryable, Identifiable, PartialEq, Debug)]
-#[table_name="metadata"]
-pub struct Metadata {
- pub id: i32,
- pub created: NaiveDateTime,
- pub created_by: i64,
-}
-
-impl Metadata {
- pub fn create(conn: &PgConnection, by_user: u64) -> Result<Metadata> {
- ::diesel::insert_into(metadata::table)
- .values(&NewMetadata {
- created_by: by_user as i64,
- })
- .get_result::<Metadata>(conn)
- .map_err(Error::from)
- }
-
- pub fn find(conn: &PgConnection, id: i32) -> Result<Metadata> {
- metadata::table.find(id)
- .get_result::<Metadata>(conn)
- .map_err(Error::from)
- }
-}
-
-#[derive(Insertable, PartialEq, Debug)]
-#[table_name="metadata"]
-pub struct NewMetadata {
- pub created_by: i64,
-}
-
-
-#[derive(Queryable, Identifiable, PartialEq, Debug)]
-#[table_name="audit_records"]
-pub struct AuditRecord {
- pub id: i32,
- pub updated: NaiveDateTime,
- pub updated_by: i64,
- pub metadata_id: i32,
-}
-
-impl AuditRecord {
- pub fn create(conn: &PgConnection, metadata: i32, by_user: u64) -> Result<AuditRecord> {
- ::diesel::insert_into(audit_records::table)
- .values(&NewAuditRecord {
- updated_by: by_user as i64,
- metadata_id: metadata,
- })
- .get_result::<AuditRecord>(conn)
- .map_err(Error::from)
- }
-}
-
-#[derive(Insertable, PartialEq, Debug)]
-#[table_name="audit_records"]
-pub struct NewAuditRecord {
- pub updated_by: i64,
- pub metadata_id: i32,
-}
-
-#[derive(Queryable, Identifiable, PartialEq, Debug)]
-#[table_name="tombstones"]
-pub struct Tombstone {
- pub id: i32,
- pub deleted: NaiveDateTime,
- pub deleted_by: i64,
- pub metadata_id: i32,
- pub meme_id: i32,
-}
-
-
-#[derive(Insertable, PartialEq, Debug)]
-#[table_name="tombstones"]
-pub struct NewTombstone {
- pub deleted_by: i64,
- pub metadata_id: i32,
- pub meme_id: i32,
-}
+use chrono::naive::NaiveDateTime; +use diesel::prelude::*; + +use crate::{ + db::schema::*, + Error, + Result, +}; + +#[derive(Queryable, Identifiable, PartialEq, Debug)] +#[table_name="memes"] +pub struct Meme { + pub id: i32, + pub title: String, + pub content: Option<String>, + pub image_id: Option<i32>, + pub audio_id: Option<i32>, + pub metadata_id: i32, +} + +impl Meme { + pub fn image(&self, conn: &PgConnection) -> Option<Result<Image>> { + self.image_id.map(|x: i32| images::table.filter(images::id.eq(x)).first(conn).map_err(Error::from)) + } + + pub fn audio(&self, conn: &PgConnection) -> Option<Result<Audio>> { + self.audio_id.map(|x: i32| audio::table.filter(audio::id.eq(x)).first(conn).map_err(Error::from)) + } + + pub fn find(conn: &PgConnection, id: i32) -> Result<Meme> { + memes::table.find(id).get_result(conn).map_err(Error::from) + } +} + +#[derive(Insertable, PartialEq, Debug)] +#[table_name="memes"] +pub struct NewMeme { + pub title: String, + pub content: Option<String>, + pub image_id: Option<i32>, + pub audio_id: Option<i32>, + pub metadata_id: i32, +} + +impl NewMeme { + pub fn save(mut self, conn: &PgConnection, by_user: u64) -> Result<Meme> { + let metadata = Metadata::create(conn, by_user)?; + + self.metadata_id = metadata.id; + + ::diesel::insert_into(memes::table) + .values(&self) + .get_result::<Meme>(conn) + .map_err(Error::from) + } +} + + +#[derive(Queryable, Identifiable, PartialEq, Debug)] +#[table_name="audio"] +pub struct Audio { + pub id: i32, + pub data: Vec<u8>, + pub metadata_id: i32, + pub data_hash: Vec<u8>, +} + +impl Audio { + pub fn create(conn: &PgConnection, data: Vec<u8>, by_user: u64) -> Result<i32> { + let mut data_hash = ::sha1::Sha1::new(); + data_hash.update(&data); + let data_hash = data_hash.digest().bytes().to_vec(); + + let id = audio::table + .select(audio::id) + .filter(audio::data_hash.eq(&data_hash)) + .get_results::<i32>(conn)?; + + if let Some(id) = id.first() { + return Ok(*id); + } + + let metadata = Metadata::create(conn, by_user)?; + + 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) + .map_err(Error::from) + } +} + +#[derive(Insertable, PartialEq, Debug)] +#[table_name="audio"] +pub struct NewAudio { + pub data: Vec<u8>, + pub metadata_id: i32, + pub data_hash: Vec<u8>, +} + + +#[derive(Queryable, Identifiable, PartialEq, Debug)] +#[table_name="images"] +pub struct Image { + pub id: i32, + pub data: Vec<u8>, + pub metadata_id: i32, + pub data_hash: Vec<u8>, + pub filename: String, +} + +impl Image { + pub fn create(conn: &PgConnection, filename: &str, data: Vec<u8>, by_user: u64) -> Result<i32> { + let mut data_hash = ::sha1::Sha1::new(); + data_hash.update(&data); + let data_hash = data_hash.digest().bytes().to_vec(); + + let id = images::table + .select(images::id) + .filter(images::data_hash.eq(&data_hash)) + .get_results::<i32>(conn)?; + + if let Some(id) = id.first() { + return Ok(*id); + } + + let metadata = Metadata::create(conn, by_user)?; + + 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) + .map_err(Error::from) + } +} + +#[derive(Insertable, PartialEq, Debug)] +#[table_name="images"] +pub struct NewImage { + pub data: Vec<u8>, + pub metadata_id: i32, + pub data_hash: Vec<u8>, + pub filename: String, +} + + +#[derive(Queryable, Identifiable, PartialEq, Debug)] +#[table_name="metadata"] +pub struct Metadata { + pub id: i32, + pub created: NaiveDateTime, + pub created_by: i64, +} + +impl Metadata { + pub fn create(conn: &PgConnection, by_user: u64) -> Result<Metadata> { + ::diesel::insert_into(metadata::table) + .values(&NewMetadata { + created_by: by_user as i64, + }) + .get_result::<Metadata>(conn) + .map_err(Error::from) + } + + pub fn find(conn: &PgConnection, id: i32) -> Result<Metadata> { + metadata::table.find(id) + .get_result::<Metadata>(conn) + .map_err(Error::from) + } +} + +#[derive(Insertable, PartialEq, Debug)] +#[table_name="metadata"] +pub struct NewMetadata { + pub created_by: i64, +} + + +#[derive(Queryable, Identifiable, PartialEq, Debug)] +#[table_name="audit_records"] +pub struct AuditRecord { + pub id: i32, + pub updated: NaiveDateTime, + pub updated_by: i64, + pub metadata_id: i32, +} + +impl AuditRecord { + pub fn create(conn: &PgConnection, metadata: i32, by_user: u64) -> Result<AuditRecord> { + ::diesel::insert_into(audit_records::table) + .values(&NewAuditRecord { + updated_by: by_user as i64, + metadata_id: metadata, + }) + .get_result::<AuditRecord>(conn) + .map_err(Error::from) + } +} + +#[derive(Insertable, PartialEq, Debug)] +#[table_name="audit_records"] +pub struct NewAuditRecord { + pub updated_by: i64, + pub metadata_id: i32, +} + +#[derive(Queryable, Identifiable, PartialEq, Debug)] +#[table_name="tombstones"] +pub struct Tombstone { + pub id: i32, + pub deleted: NaiveDateTime, + pub deleted_by: i64, + pub metadata_id: i32, + pub meme_id: i32, +} + + +#[derive(Insertable, PartialEq, Debug)] +#[table_name="tombstones"] +pub struct NewTombstone { + pub deleted_by: i64, + pub metadata_id: i32, + pub meme_id: i32, +} |
