sheet-organizer/src/sheet_dao.rs

139 lines
4.5 KiB
Rust

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<Path>,
) -> sqlx::Result<Sheet> {
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<PathBuf> {
// sqlx::query("SELECT path FROM books WHERE id = $1")
// .bind(book_id)
// .map(|row: SqliteRow| PathBuf::try_from(row.try_get::<String, _>("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<Composer> {
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<Vec<Sheet>> {
let mut sheets: Vec<Sheet> = 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::<i64, _>("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<SheetKind> {
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<i64> {
s.trim()
.split(',')
.map(|s| s.parse::<i64>().unwrap())
.collect()
}
fn parse_pdf_from_row(row: &SqliteRow) -> sqlx::Result<Pdf> {
// TODO: use get instead of try_get???
Ok(Pdf {
path: PathBuf::from(row.try_get::<String, _>("path").unwrap()),
file_size: row.try_get::<i64, _>("file_size")? as u64,
file_hash: row.try_get("file_hash")?,
})
}