Remove BookSheet and make it normal Sheet

Simplifies code a lot
This commit is contained in:
Julian Mutter 2024-02-11 09:35:32 +01:00
parent d3f2375995
commit 42b7d422a8
6 changed files with 48 additions and 113 deletions

View File

@ -1,11 +1,8 @@
CREATE TABLE IF NOT EXISTS sheets (id INTEGER PRIMARY KEY AUTOINCREMENT, CREATE TABLE IF NOT EXISTS sheets (id INTEGER PRIMARY KEY AUTOINCREMENT,
last_opened INTEGER, name TEXT, composer_id INTEGER, path TEXT, file_size INTEGER, file_hash TEXT); last_opened INTEGER, name TEXT, composer_id INTEGER, first_page INTEGER, book_id INTEGER, path TEXT, file_size INTEGER, file_hash TEXT);
CREATE TABLE IF NOT EXISTS orphans (id INTEGER PRIMARY KEY AUTOINCREMENT, CREATE TABLE IF NOT EXISTS orphans (id INTEGER PRIMARY KEY AUTOINCREMENT,
last_opened INTEGER, path TEXT, file_size INTEGER, file_hash TEXT); last_opened INTEGER, path TEXT, file_size INTEGER, file_hash TEXT);
CREATE TABLE IF NOT EXISTS books (id INTEGER PRIMARY KEY AUTOINCREMENT, CREATE TABLE IF NOT EXISTS books (id INTEGER PRIMARY KEY AUTOINCREMENT,
last_opened INTEGER, name TEXT, composer_id INTEGER, sheet_ids TEXT, path TEXT, file_size INTEGER, file_hash TEXT); last_opened INTEGER, name TEXT, composer_id INTEGER, sheet_ids TEXT, path TEXT, file_size INTEGER, file_hash TEXT);
CREATE TABLE IF NOT EXISTS booksheets (id INTEGER PRIMARY KEY AUTOINCREMENT,
last_opened INTEGER, name TEXT, book_id INTEGER, first_page INTEGER, last_page INTEGER);
CREATE TABLE IF NOT EXISTS composers (id INTEGER primary key autoincrement, name TEXT); CREATE TABLE IF NOT EXISTS composers (id INTEGER primary key autoincrement, name TEXT);

View File

@ -85,7 +85,7 @@ impl FileValidationResult {
fn validate_sheet_files(sheets: Vec<Sheet>, dir: impl AsRef<Path>) -> FileValidationResult { fn validate_sheet_files(sheets: Vec<Sheet>, dir: impl AsRef<Path>) -> FileValidationResult {
let (validated_sheets, mut invalidated_sheets): (Vec<_>, Vec<_>) = sheets let (validated_sheets, mut invalidated_sheets): (Vec<_>, Vec<_>) = sheets
.into_iter() .into_iter()
.partition(|sheet| sheet.validate_own_path().unwrap_or(false)); .partition(|sheet| sheet.pdf.validate_own_path().unwrap_or(false));
let mut updated_sheets = Vec::new(); let mut updated_sheets = Vec::new();
let mut unassigned_files = Vec::new(); let mut unassigned_files = Vec::new();
@ -95,15 +95,15 @@ fn validate_sheet_files(sheets: Vec<Sheet>, dir: impl AsRef<Path>) -> FileValida
if let Some((i, _)) = invalidated_sheets if let Some((i, _)) = invalidated_sheets
.iter() .iter()
.enumerate() .enumerate()
.find(|(_, sheet)| sheet.validate_path(&pdf_file).unwrap_or(false)) .find(|(_, sheet)| sheet.pdf.validate_path(&pdf_file).unwrap_or(false))
{ {
let mut sheet = invalidated_sheets.remove(i); let mut sheet = invalidated_sheets.remove(i);
let new_pdf = Pdf::try_from(pdf_file).unwrap(); let new_pdf = Pdf::try_from(pdf_file).unwrap();
sheet.update_pdf_file(new_pdf); sheet.pdf = new_pdf;
updated_sheets.push(sheet); updated_sheets.push(sheet);
} else if !validated_sheets } else if !validated_sheets
.iter() .iter()
.any(|sheet| sheet.pdf_path_equal(&pdf_file)) .any(|sheet| sheet.pdf.path == pdf_file)
{ {
unassigned_files.push(pdf_file); unassigned_files.push(pdf_file);
} }

View File

@ -1,16 +1,12 @@
use std::{ use std::{
cmp::Ordering, cmp::Ordering,
ffi::{OsStr, OsString},
fs, fs,
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
use chrono::{DateTime, NaiveDateTime, Utc}; use chrono::{DateTime, NaiveDateTime, Utc};
use sqlx::database;
use strum_macros::{EnumDiscriminants, EnumIter}; use strum_macros::{EnumDiscriminants, EnumIter};
use crate::{database::Database, sheet_dao};
pub trait PdfSheet { pub trait PdfSheet {
fn get_pdf(&self) -> &Pdf; fn get_pdf(&self) -> &Pdf;
} }
@ -20,31 +16,24 @@ pub struct Sheet {
pub id: i64, pub id: i64,
pub last_opened: I64DateTime, pub last_opened: I64DateTime,
pub kind: SheetKind, pub kind: SheetKind,
pub pdf: Pdf,
} }
#[derive(Debug, Clone, PartialEq, Eq, EnumDiscriminants)] #[derive(Debug, Clone, PartialEq, Eq, EnumDiscriminants)]
#[strum_discriminants(derive(EnumIter))] #[strum_discriminants(derive(EnumIter))]
pub enum SheetKind { pub enum SheetKind {
Sheet { Sheet {
pdf: Pdf,
name: String, name: String,
composer_id: i64, composer_id: i64,
first_page: i64,
book_id: Option<i64>,
}, },
Orphan { Orphan,
pdf: Pdf,
},
Book { Book {
pdf: Pdf,
name: String, name: String,
composer_id: i64, composer_id: i64,
sheet_ids: Vec<i64>, sheet_ids: Vec<i64>,
}, },
BookSheet {
name: String,
book_id: i64,
first_page: i64,
last_page: i64,
},
} }
impl PartialOrd for Sheet { impl PartialOrd for Sheet {
@ -60,62 +49,17 @@ impl Ord for Sheet {
} }
impl Sheet { impl Sheet {
pub async fn open_file(&self, database: &Database) { pub fn open_file(&self) {
let path = match &self.kind { let path = &self.pdf.path;
SheetKind::Sheet { pdf, .. } => pdf.path.clone(), // TODO: open on first_page
SheetKind::Orphan { pdf } => pdf.path.clone(),
SheetKind::Book { pdf, .. } => pdf.path.clone(),
SheetKind::BookSheet { book_id, .. } => sheet_dao::find_path_of_book(database, book_id)
.await
.unwrap(),
};
opener::open(path).unwrap(); opener::open(path).unwrap();
} }
pub fn update_pdf_file(&mut self, new_pdf: Pdf) -> std::io::Result<()> { pub fn is_part_of_book(&self) -> bool {
match &mut self.kind { if let SheetKind::Sheet { book_id, .. } = &self.kind {
SheetKind::Sheet { pdf, .. } => *pdf = new_pdf, return book_id.is_some();
SheetKind::Orphan { pdf, .. } => *pdf = new_pdf,
SheetKind::Book { pdf, .. } => *pdf = new_pdf,
SheetKind::BookSheet { .. } => {} // TODO: find better solution!
};
Ok(())
}
pub fn pdf_path_equal(&self, path: impl AsRef<Path>) -> bool {
match &self.kind {
SheetKind::Sheet { pdf, .. } => pdf.path == path.as_ref(),
SheetKind::Orphan { pdf, .. } => pdf.path == path.as_ref(),
SheetKind::Book { pdf, .. } => pdf.path == path.as_ref(),
SheetKind::BookSheet { .. } => false, // TODO: find better solution!
} }
} false
pub fn validate_own_path(&self) -> std::io::Result<bool> {
Ok(match &self.kind {
SheetKind::Sheet { pdf, .. } => pdf.validate_path(&pdf.path)?,
SheetKind::Orphan { pdf, .. } => pdf.validate_path(&pdf.path)?,
SheetKind::Book { pdf, .. } => pdf.validate_path(&pdf.path)?,
SheetKind::BookSheet { book_id, .. } => true, // TODO: better solution?
})
}
pub fn try_get_path(&self) -> Option<&Path> {
match &self.kind {
SheetKind::Sheet { pdf, .. } => Some(&pdf.path),
SheetKind::Orphan { pdf, .. } => Some(&pdf.path),
SheetKind::Book { pdf, .. } => Some(&pdf.path),
SheetKind::BookSheet { .. } => None,
}
}
pub fn validate_path(&self, path: impl AsRef<Path>) -> std::io::Result<bool> {
Ok(match &self.kind {
SheetKind::Sheet { pdf, .. } => pdf.validate_path(path)?,
SheetKind::Orphan { pdf, .. } => pdf.validate_path(path)?,
SheetKind::Book { pdf, .. } => pdf.validate_path(path)?,
SheetKind::BookSheet { book_id, .. } => true, // TODO: better solution?
})
} }
} }
@ -125,7 +69,6 @@ impl SheetKindDiscriminants {
SheetKindDiscriminants::Sheet => "sheets", SheetKindDiscriminants::Sheet => "sheets",
SheetKindDiscriminants::Orphan => "orphans", SheetKindDiscriminants::Orphan => "orphans",
SheetKindDiscriminants::Book => "books", SheetKindDiscriminants::Book => "books",
SheetKindDiscriminants::BookSheet => "booksheets",
} }
} }
} }
@ -169,6 +112,10 @@ impl Pdf {
self.path.file_name().unwrap().to_str().unwrap() self.path.file_name().unwrap().to_str().unwrap()
} }
pub fn validate_own_path(&self) -> std::io::Result<bool> {
self.validate_path(&self.path)
}
pub fn validate_path(&self, path: impl AsRef<Path>) -> std::io::Result<bool> { pub fn validate_path(&self, path: impl AsRef<Path>) -> std::io::Result<bool> {
// First compare file size since it is faster than hashing // First compare file size since it is faster than hashing
let file_size = fs::metadata(path.as_ref())?.len(); let file_size = fs::metadata(path.as_ref())?.len();

View File

@ -35,31 +35,30 @@ pub async fn insert_file_as_orphan(
Ok(Sheet { Ok(Sheet {
id, id,
pdf,
last_opened: I64DateTime(last_opened), last_opened: I64DateTime(last_opened),
kind: SheetKind::Orphan { pdf }, kind: SheetKind::Orphan,
}) })
} }
pub async fn find_path_of_book(database: &Database, book_id: &i64) -> sqlx::Result<PathBuf> { // pub async fn find_path_of_book(database: &Database, book_id: &i64) -> sqlx::Result<PathBuf> {
sqlx::query("SELECT path FROM books WHERE id = $1") // sqlx::query("SELECT path FROM books WHERE id = $1")
.bind(book_id) // .bind(book_id)
.map(|row: SqliteRow| PathBuf::try_from(row.try_get::<String, _>("path").unwrap()).unwrap()) // .map(|row: SqliteRow| PathBuf::try_from(row.try_get::<String, _>("path").unwrap()).unwrap())
.fetch_one(&database.connection) // .fetch_one(&database.connection)
.await // .await
} // }
pub async fn update_sheet_path(database: &Database, sheet: &Sheet) -> sqlx::Result<()> { pub async fn update_sheet_path(database: &Database, sheet: &Sheet) -> sqlx::Result<()> {
if let Some(path) = sheet.try_get_path() { // TODO: when updating book or sheet of book, update all
let sheet_kind = SheetKindDiscriminants::from(&sheet.kind); let sheet_kind = SheetKindDiscriminants::from(&sheet.kind);
let table = sheet_kind.get_database_table_name(); let table = sheet_kind.get_database_table_name();
return sqlx::query(&format!("UPDATE {} SET path = $1 WHERE id = $2", table)) return sqlx::query(&format!("UPDATE {} SET path = $1 WHERE id = $2", table))
.bind(path.to_str().unwrap().to_string()) .bind(sheet.pdf.path.to_str().unwrap().to_string())
.bind(sheet.id) .bind(sheet.id)
.execute(&database.connection) .execute(&database.connection)
.await .await
.map(|_| ()); .map(|_| ());
}
Ok(()) // TODO: error on else?
} }
pub async fn update_sheet_last_opened(database: &Database, sheet: &Sheet) -> sqlx::Result<()> { pub async fn update_sheet_last_opened(database: &Database, sheet: &Sheet) -> sqlx::Result<()> {
@ -87,6 +86,7 @@ pub async fn fetch_all_sheets(database: &Database) -> sqlx::Result<Vec<Sheet>> {
id: row.try_get("id").unwrap(), id: row.try_get("id").unwrap(),
last_opened: I64DateTime::try_from(row.try_get::<i64, _>("last_opened").unwrap()) last_opened: I64DateTime::try_from(row.try_get::<i64, _>("last_opened").unwrap())
.unwrap(), .unwrap(),
pdf: parse_pdf_from_row(&row).unwrap(),
kind: parse_kind_from_row(kind, row).unwrap(), kind: parse_kind_from_row(kind, row).unwrap(),
}) })
.fetch_all(&database.connection) .fetch_all(&database.connection)
@ -103,23 +103,15 @@ fn parse_kind_from_row(kind: SheetKindDiscriminants, row: SqliteRow) -> sqlx::Re
SheetKindDiscriminants::Sheet => SheetKind::Sheet { SheetKindDiscriminants::Sheet => SheetKind::Sheet {
name: row.try_get("name")?, name: row.try_get("name")?,
composer_id: row.try_get("composer_id")?, composer_id: row.try_get("composer_id")?,
pdf: parse_pdf_from_row(&row)?, first_page: row.try_get("first_page")?,
}, book_id: row.try_get("book_id").ok(),
SheetKindDiscriminants::Orphan => SheetKind::Orphan {
pdf: parse_pdf_from_row(&row)?,
}, },
SheetKindDiscriminants::Orphan => SheetKind::Orphan,
SheetKindDiscriminants::Book => SheetKind::Book { SheetKindDiscriminants::Book => SheetKind::Book {
name: row.try_get("name")?, name: row.try_get("name")?,
composer_id: row.try_get("composer_id")?, composer_id: row.try_get("composer_id")?,
pdf: parse_pdf_from_row(&row)?,
sheet_ids: sheet_ids_from_string(row.try_get("sheet_ids").unwrap()), sheet_ids: sheet_ids_from_string(row.try_get("sheet_ids").unwrap()),
}, },
SheetKindDiscriminants::BookSheet => SheetKind::BookSheet {
name: row.try_get("name")?,
book_id: row.try_get("book_id")?,
first_page: row.try_get("first_page")?,
last_page: row.try_get("last_page")?,
},
}) })
} }

View File

@ -112,8 +112,8 @@ impl AsyncComponent for AppModel {
.emit(SheetListingInput::Query(query.clone())); .emit(SheetListingInput::Query(query.clone()));
} }
AppInput::SheetPressed(sheet) => { AppInput::SheetPressed(sheet) => {
sheet.open_file(&self.database).await; sheet.open_file();
// TODO: updating does not work // TODO: updating directly does not work
let mut sheet = sheet; let mut sheet = sheet;
sheet.last_opened = I64DateTime(Utc::now()); sheet.last_opened = I64DateTime(Utc::now());
sheet_dao::update_sheet_last_opened(&self.database, &sheet) sheet_dao::update_sheet_last_opened(&self.database, &sheet)

View File

@ -46,17 +46,16 @@ impl FactoryComponent for SheetModel {
} }
} }
fn init_model(value: Self::Init, _index: &DynamicIndex, _sender: FactorySender<Self>) -> Self { fn init_model(sheet: Self::Init, _index: &DynamicIndex, _sender: FactorySender<Self>) -> Self {
let label = match &value.kind { let label = match &sheet.kind {
crate::sheet::SheetKind::Sheet { name, .. } => name, crate::sheet::SheetKind::Sheet { name, .. } => name,
crate::sheet::SheetKind::Orphan { pdf } => pdf.get_name(), crate::sheet::SheetKind::Orphan {} => sheet.pdf.get_name(),
crate::sheet::SheetKind::Book { name, .. } => name, crate::sheet::SheetKind::Book { name, .. } => name,
crate::sheet::SheetKind::BookSheet { name, .. } => name,
}; };
SheetModel { SheetModel {
label: label.to_string(), label: label.to_string(),
sheet: value, sheet,
visible: true, visible: true,
} }
} }