use super::*; #[derive(Debug, Clone, Copy)] pub enum FileOwnerKind { Pad, Note, } #[derive(Debug, Clone, Copy)] pub struct FileOwner { pub kind: FileOwnerKind, pub id: i64, } pub async fn find_file_owner( pool: &Database, token: &str, ) -> Result, sqlx::Error> { if let Some(id) = sqlx::query_scalar::<_, i64>(queries::get(pool.kind(), queries::Q026)) .bind(token) .fetch_optional(pool.pool()) .await? { return Ok(Some(FileOwner { kind: FileOwnerKind::Pad, id, })); } if let Some(id) = sqlx::query_scalar::<_, i64>(queries::get(pool.kind(), queries::Q027)) .bind(token) .fetch_optional(pool.pool()) .await? { return Ok(Some(FileOwner { kind: FileOwnerKind::Note, id, })); } Ok(None) } #[derive(Debug, Clone, Serialize)] pub struct NoteFile { pub id: i64, pub filename: String, pub url: String, pub mime_type: String, pub size_bytes: i64, pub created_at: String, pub is_attached: bool, pub detached_at: Option, } #[derive(Debug, Clone, FromRow)] struct SqliteNoteFile { id: i64, filename: String, url: String, mime_type: String, size_bytes: i64, created_at: String, is_attached: i64, detached_at: Option, } impl From for NoteFile { fn from(value: SqliteNoteFile) -> Self { Self { id: value.id, filename: value.filename, url: value.url, mime_type: value.mime_type, size_bytes: value.size_bytes, created_at: value.created_at, is_attached: value.is_attached != 0, detached_at: value.detached_at, } } } pub async fn delete_note(pool: &Database, note_id: i64) -> Result<(), sqlx::Error> { sqlx::query(queries::get(pool.kind(), queries::Q031)) .bind(note_id) .execute(pool.pool()) .await?; Ok(()) } pub async fn register_note_file( pool: &Database, note_id: i64, filename: &str, url: &str, mime_type: &str, size_bytes: i64, ) -> Result<(), sqlx::Error> { sqlx::query(queries::get(pool.kind(), queries::Q032)) .bind(note_id) .bind(filename) .bind(url) .bind(mime_type) .bind(size_bytes) .execute(pool.pool()) .await?; Ok(()) } pub async fn list_note_files(pool: &Database, note_id: i64) -> Result, sqlx::Error> { list_files(pool, queries::Q033, note_id).await } async fn list_files( pool: &Database, query: queries::Query, owner_id: i64, ) -> Result, sqlx::Error> { if pool.kind() == DatabaseKind::Sqlite { return Ok( sqlx::query_as::<_, SqliteNoteFile>(queries::get(pool.kind(), query)) .bind(owner_id) .fetch_all(pool.pool()) .await? .into_iter() .map(NoteFile::from) .collect(), ); } sqlx::query_as::<_, NoteFile>(queries::get(pool.kind(), query)) .bind(owner_id) .fetch_all(pool.pool()) .await } pub async fn set_note_file_attached( pool: &Database, file_id: i64, attached: bool, ) -> Result<(), sqlx::Error> { let detached_at: Option = if attached { None } else { Some(chrono::Utc::now().to_rfc3339()) }; sqlx::query(queries::get(pool.kind(), queries::Q034)) .bind(attached) .bind(detached_at) .bind(file_id) .execute(pool.pool()) .await?; Ok(()) } pub async fn register_pad_file( pool: &Database, pad_id: i64, filename: &str, url: &str, mime_type: &str, size_bytes: i64, ) -> Result<(), sqlx::Error> { sqlx::query(queries::get(pool.kind(), queries::Q035)) .bind(pad_id) .bind(filename) .bind(url) .bind(mime_type) .bind(size_bytes) .execute(pool.pool()) .await?; Ok(()) } pub async fn list_pad_files(pool: &Database, pad_id: i64) -> Result, sqlx::Error> { list_files(pool, queries::Q036, pad_id).await } pub async fn set_pad_file_attached( pool: &Database, file_id: i64, attached: bool, ) -> Result<(), sqlx::Error> { let detached_at: Option = if attached { None } else { Some(chrono::Utc::now().to_rfc3339()) }; sqlx::query(queries::get(pool.kind(), queries::Q037)) .bind(attached) .bind(detached_at) .bind(file_id) .execute(pool.pool()) .await?; Ok(()) } pub async fn find_note_file( pool: &Database, note_id: i64, file_id: i64, ) -> Result, sqlx::Error> { if pool.kind() == DatabaseKind::Sqlite { return Ok( sqlx::query_as::<_, SqliteNoteFile>(queries::get(pool.kind(), queries::Q038)) .bind(file_id) .bind(note_id) .fetch_optional(pool.pool()) .await? .map(NoteFile::from), ); } sqlx::query_as::<_, NoteFile>(queries::get(pool.kind(), queries::Q038)) .bind(file_id) .bind(note_id) .fetch_optional(pool.pool()) .await } pub async fn delete_note_file( pool: &Database, note_id: i64, file_id: i64, ) -> Result<(), sqlx::Error> { sqlx::query(queries::get(pool.kind(), queries::Q039)) .bind(file_id) .bind(note_id) .execute(pool.pool()) .await?; Ok(()) } pub async fn find_pad_file( pool: &Database, pad_id: i64, file_id: i64, ) -> Result, sqlx::Error> { if pool.kind() == DatabaseKind::Sqlite { return Ok( sqlx::query_as::<_, SqliteNoteFile>(queries::get(pool.kind(), queries::Q046)) .bind(file_id) .bind(pad_id) .fetch_optional(pool.pool()) .await? .map(NoteFile::from), ); } sqlx::query_as::<_, NoteFile>(queries::get(pool.kind(), queries::Q046)) .bind(file_id) .bind(pad_id) .fetch_optional(pool.pool()) .await } pub async fn delete_pad_file( pool: &Database, pad_id: i64, file_id: i64, ) -> Result<(), sqlx::Error> { sqlx::query(queries::get(pool.kind(), queries::Q047)) .bind(file_id) .bind(pad_id) .execute(pool.pool()) .await?; Ok(()) } impl<'r> sqlx::FromRow<'r, AnyRow> for NoteFile { fn from_row(row: &'r AnyRow) -> Result { let attached: i64 = row.try_get("is_attached")?; Ok(Self { id: row.try_get("id")?, filename: crate::row_decode::text(row, "filename")?, url: crate::row_decode::text(row, "url")?, mime_type: crate::row_decode::text(row, "mime_type")?, size_bytes: row.try_get("size_bytes")?, created_at: crate::row_decode::text(row, "created_at")?, is_attached: attached != 0, detached_at: crate::row_decode::optional_text(row, "detached_at")?, }) } }