use crate::{ database::Database, sheet::{Composer, I64DateTime, Pdf, Sheet, SheetKind, SheetKindDiscriminants}, }; use sqlx::{sqlite::SqliteRow, Row}; use std::path::{Path, PathBuf}; use strum::IntoEnumIterator; pub async fn insert_file_as_orphan( database: &Database, file: impl AsRef, ) -> sqlx::Result { let pdf = Pdf::try_from(file.as_ref().to_path_buf()).unwrap(); let last_opened = chrono::offset::Utc::now(); let result = sqlx::query( " INSERT INTO orphans (path, file_size, file_hash, last_opened) VALUES ($1, $2, $3, $4) ", ) .bind(pdf.path.to_str().unwrap().to_string()) .bind(pdf.file_size as i32) .bind(pdf.file_hash.clone()) .bind(last_opened.timestamp()) .execute(&database.connection) .await .unwrap(); let id = result.last_insert_rowid(); Ok(Sheet { id, pdf, last_opened: I64DateTime(last_opened), kind: SheetKind::Orphan, }) } // pub async fn find_path_of_book(database: &Database, book_id: &i64) -> sqlx::Result { // sqlx::query("SELECT path FROM books WHERE id = $1") // .bind(book_id) // .map(|row: SqliteRow| PathBuf::try_from(row.try_get::("path").unwrap()).unwrap()) // .fetch_one(&database.connection) // .await // } pub async fn update_sheet_path(database: &Database, sheet: &Sheet) -> sqlx::Result<()> { // TODO: when updating book or sheet of book, update all let sheet_kind = SheetKindDiscriminants::from(&sheet.kind); let table = sheet_kind.get_database_table_name(); sqlx::query(&format!("UPDATE {} SET path = $1 WHERE id = $2", table)) .bind(sheet.pdf.path.to_str().unwrap().to_string()) .bind(sheet.id) .execute(&database.connection) .await .map(|_| ()) } pub async fn update_sheet_last_opened(database: &Database, sheet: &Sheet) -> sqlx::Result<()> { let sheet_kind = SheetKindDiscriminants::from(&sheet.kind); let table = sheet_kind.get_database_table_name(); sqlx::query(&format!( "UPDATE {} SET last_opened = $1 WHERE id = $2", table )) .bind(i64::from(&sheet.last_opened)) .bind(sheet.id) .execute(&database.connection) .await .map(|_| ()) } pub async fn get_composer_by_id(database: &Database, id: i64) -> sqlx::Result { sqlx::query(&format!("SELECT * FROM {} WHERE id = {}", "composers", id)) .map(|row: SqliteRow| Composer { id, name: row.try_get("name").unwrap(), }) .fetch_one(&database.connection) .await } pub async fn fetch_all_sheets(database: &Database) -> sqlx::Result> { let mut sheets: Vec = Vec::new(); for kind in SheetKindDiscriminants::iter() { let table = kind.get_database_table_name(); let mut sheets_of_kind = sqlx::query(&format!("SELECT * FROM {}", table)) .map(|row: SqliteRow| Sheet { id: row.try_get("id").unwrap(), last_opened: I64DateTime::try_from(row.try_get::("last_opened").unwrap()) .unwrap(), pdf: parse_pdf_from_row(&row).unwrap(), kind: parse_kind_from_row(kind, row).unwrap(), }) .fetch_all(&database.connection) .await?; sheets.append(&mut sheets_of_kind); } Ok(sheets) } fn parse_kind_from_row(kind: SheetKindDiscriminants, row: SqliteRow) -> sqlx::Result { Ok(match kind { SheetKindDiscriminants::Sheet => SheetKind::Sheet { name: row.try_get("name")?, composer_id: row.try_get("composer_id")?, first_page: row.try_get("first_page")?, book_id: row.try_get("book_id").ok(), }, SheetKindDiscriminants::Orphan => SheetKind::Orphan, SheetKindDiscriminants::Book => SheetKind::Book { name: row.try_get("name")?, composer_id: row.try_get("composer_id")?, sheet_ids: sheet_ids_from_string(row.try_get("sheet_ids").unwrap()), }, }) } fn sheet_ids_from_string(s: String) -> Vec { s.trim() .split(',') .map(|s| s.parse::().unwrap()) .collect() } fn parse_pdf_from_row(row: &SqliteRow) -> sqlx::Result { // TODO: use get instead of try_get??? Ok(Pdf { path: PathBuf::from(row.try_get::("path").unwrap()), file_size: row.try_get::("file_size")? as u64, file_hash: row.try_get("file_hash")?, }) }