Modify Sheet struct, add OrphanFile table and db
This commit is contained in:
parent
7120520347
commit
b102906f11
68
Cargo.lock
generated
68
Cargo.lock
generated
@ -45,6 +45,21 @@ version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
|
||||
|
||||
[[package]]
|
||||
name = "android-tzdata"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.11"
|
||||
@ -290,6 +305,20 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f13690e35a5e4ace198e7beea2895d29f3a9cc55015fcebe6336bd2010af9eb"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"wasm-bindgen",
|
||||
"windows-targets 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.4.18"
|
||||
@ -348,6 +377,12 @@ version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.12"
|
||||
@ -1044,6 +1079,29 @@ version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.5.0"
|
||||
@ -1798,6 +1856,7 @@ name = "sheet-organizer"
|
||||
version = "0.1.1"
|
||||
dependencies = [
|
||||
"blake3",
|
||||
"chrono",
|
||||
"clap",
|
||||
"dotenvy",
|
||||
"env_logger",
|
||||
@ -2539,6 +2598,15 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
|
@ -25,6 +25,7 @@ sqlx = { version = "0.7", features = [ "runtime-tokio", "sqlite", "migrate", "ma
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
blake3 = "1.5.0"
|
||||
dotenvy = "0.15.7"
|
||||
chrono = "0.4.33"
|
||||
|
||||
[profile.dev.package.sqlx-macros]
|
||||
opt-level = 3
|
||||
|
@ -1,2 +1,4 @@
|
||||
CREATE TABLE IF NOT EXISTS sheets (id integer primary key autoincrement, name TEXT, path TEXT, file_size INTEGER, file_hash TEXT);
|
||||
CREATE TABLE IF NOT EXISTS sheets (id integer primary key autoincrement, name TEXT, composer_id integer, path TEXT, file_size INTEGER, file_hash TEXT, last_opened INTEGER);
|
||||
CREATE TABLE IF NOT EXISTS composers (id integer primary key autoincrement, name TEXT);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS orphan_files (id integer primary key autoincrement, path TEXT, file_size INTEGER, file_hash TEXT, last_opened INTEGER);
|
||||
|
@ -1,7 +1,7 @@
|
||||
use log::debug;
|
||||
use sqlx::{migrate::MigrateDatabase, Sqlite, SqlitePool};
|
||||
|
||||
use crate::sheet::Sheet;
|
||||
use crate::sheet::{OrphanFile, Sheet};
|
||||
|
||||
pub struct Database {
|
||||
connection: SqlitePool,
|
||||
@ -36,14 +36,16 @@ impl Database {
|
||||
pub async fn _insert_sheet(&self, sheet: Sheet) -> sqlx::Result<()> {
|
||||
sqlx::query(
|
||||
"
|
||||
INSERT INTO sheets (name, path, file_size, file_hash)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
INSERT INTO sheets (name, composer_id, path, file_size, file_hash, last_modified)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
",
|
||||
)
|
||||
.bind(sheet.name)
|
||||
.bind(sheet.path)
|
||||
.bind(sheet.file_size)
|
||||
.bind(sheet.composer_id)
|
||||
.bind(sheet.path.to_str().unwrap().to_string())
|
||||
.bind(sheet.file_size as i32)
|
||||
.bind(sheet.file_hash)
|
||||
.bind(sheet.last_opened.timestamp())
|
||||
.execute(&self.connection)
|
||||
.await
|
||||
.map(|_| ())
|
||||
@ -51,7 +53,7 @@ impl Database {
|
||||
|
||||
pub async fn update_sheet_path(&self, sheet: &Sheet) -> sqlx::Result<()> {
|
||||
sqlx::query("UPDATE sheets SET path = $1 WHERE id = $2")
|
||||
.bind(sheet.path.clone())
|
||||
.bind(sheet.path.to_str().unwrap().to_string())
|
||||
.bind(sheet.id)
|
||||
.execute(&self.connection)
|
||||
.await
|
||||
@ -63,4 +65,35 @@ impl Database {
|
||||
.fetch_all(&self.connection)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn insert_orphan_file(&self, file: OrphanFile) -> sqlx::Result<()> {
|
||||
sqlx::query(
|
||||
"
|
||||
INSERT INTO orphan_files (path, file_size, file_hash, last_modified)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
",
|
||||
)
|
||||
.bind(file.path.to_str().unwrap().to_string())
|
||||
.bind(file.file_size as i32)
|
||||
.bind(file.file_hash)
|
||||
.bind(file.last_opened.timestamp())
|
||||
.execute(&self.connection)
|
||||
.await
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
pub async fn update_orphan_file_path(&self, file: &OrphanFile) -> sqlx::Result<()> {
|
||||
sqlx::query("UPDATE orphan_files SET path = $1 WHERE id = $2")
|
||||
.bind(file.path.to_str().unwrap().to_string())
|
||||
.bind(file.id)
|
||||
.execute(&self.connection)
|
||||
.await
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
pub async fn fetch_all_orphan_files(&self) -> sqlx::Result<Vec<OrphanFile>> {
|
||||
sqlx::query_as::<_, OrphanFile>("SELECT * FROM orphan_files")
|
||||
.fetch_all(&self.connection)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ fn validate_sheet_files(sheets: Vec<Sheet>, dir: impl AsRef<Path>) -> FileValida
|
||||
.find(|(_, sheet)| sheet.validate_path(&pdf_file).unwrap_or(false))
|
||||
{
|
||||
let mut sheet = invalidated_sheets.remove(i);
|
||||
sheet.path = pdf_file.to_str().unwrap().to_string();
|
||||
sheet.path = pdf_file;
|
||||
updated_sheets.push(sheet);
|
||||
} else {
|
||||
unassigned_files.push(pdf_file);
|
||||
|
66
src/sheet.rs
66
src/sheet.rs
@ -1,14 +1,40 @@
|
||||
use std::{fs, path::Path};
|
||||
use std::{
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
#[derive(sqlx::FromRow, Debug, Clone)]
|
||||
use sqlx::{prelude::*, sqlite::SqliteRow};
|
||||
// use sqlx::{FromRow, sqlite::SqliteRow, sqlx::Row};
|
||||
use chrono::{DateTime, NaiveDateTime, Utc};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Sheet {
|
||||
pub id: i32,
|
||||
pub name: String,
|
||||
// #[sqlx(from = "String")]
|
||||
pub path: String,
|
||||
// #[sqlx(from = "i64")]
|
||||
pub file_size: i32,
|
||||
pub composer_id: i32,
|
||||
pub path: PathBuf,
|
||||
pub file_size: u64,
|
||||
pub file_hash: String,
|
||||
pub last_opened: DateTime<Utc>,
|
||||
}
|
||||
|
||||
impl FromRow<'_, SqliteRow> for Sheet {
|
||||
fn from_row(row: &SqliteRow) -> sqlx::Result<Self> {
|
||||
Ok(Self {
|
||||
id: row.try_get("id")?,
|
||||
name: row.try_get("name")?,
|
||||
composer_id: row.try_get("composer_id")?,
|
||||
path: row.try_get::<&str, _>("path")?.into(),
|
||||
file_size: row.try_get::<i32, _>("file_size")? as u64,
|
||||
file_hash: row.try_get("file_hash")?,
|
||||
last_opened: NaiveDateTime::from_timestamp_opt(
|
||||
row.try_get::<i64, _>("last_opened")?,
|
||||
0,
|
||||
)
|
||||
.unwrap()
|
||||
.and_utc(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(sqlx::FromRow)]
|
||||
@ -21,7 +47,7 @@ impl Sheet {
|
||||
pub fn validate_path(&self, path: impl AsRef<Path>) -> std::io::Result<bool> {
|
||||
// First compare file size since it is faster than hashing
|
||||
let file_size = fs::metadata(path.as_ref())?.len();
|
||||
if file_size as i32 == self.file_size {
|
||||
if file_size == self.file_size {
|
||||
let file_content = fs::read(path.as_ref())?;
|
||||
let file_hash = blake3::hash(&file_content);
|
||||
if file_hash.to_string() == self.file_hash {
|
||||
@ -32,3 +58,29 @@ impl Sheet {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OrphanFile {
|
||||
pub id: i32,
|
||||
pub path: PathBuf,
|
||||
pub file_size: u64,
|
||||
pub file_hash: String,
|
||||
pub last_opened: DateTime<Utc>,
|
||||
}
|
||||
|
||||
impl FromRow<'_, SqliteRow> for OrphanFile {
|
||||
fn from_row(row: &SqliteRow) -> sqlx::Result<Self> {
|
||||
Ok(Self {
|
||||
id: row.try_get("id")?,
|
||||
path: row.try_get::<&str, _>("path")?.into(),
|
||||
file_size: row.try_get::<i32, _>("file_size")? as u64,
|
||||
file_hash: row.try_get("file_hash")?,
|
||||
last_opened: NaiveDateTime::from_timestamp_opt(
|
||||
row.try_get::<i64, _>("last_opened")?,
|
||||
0,
|
||||
)
|
||||
.unwrap()
|
||||
.and_utc(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use gtk::prelude::*;
|
||||
use relm4::prelude::*;
|
||||
@ -14,10 +14,10 @@ pub enum SheetModelType {
|
||||
}
|
||||
|
||||
impl SheetModelType {
|
||||
pub fn get_path(&self) -> &str {
|
||||
pub fn get_path(&self) -> &Path {
|
||||
match self {
|
||||
SheetModelType::Sheet { sheet } => &sheet.path,
|
||||
SheetModelType::Pdf { path } => path.to_str().unwrap(),
|
||||
SheetModelType::Sheet { sheet } => &sheet.path.as_path(),
|
||||
SheetModelType::Pdf { path } => path.as_path(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -64,9 +64,7 @@ impl FactoryComponent for SheetModel {
|
||||
fn init_model(value: Self::Init, _index: &DynamicIndex, _sender: FactorySender<Self>) -> Self {
|
||||
let label = match &value {
|
||||
SheetModelType::Sheet { sheet } => sheet.name.to_string(),
|
||||
SheetModelType::Pdf { path } => {
|
||||
path.file_name().unwrap().to_str().unwrap().to_string()
|
||||
}
|
||||
SheetModelType::Pdf { path } => path.file_name().unwrap().to_str().unwrap().to_string(),
|
||||
};
|
||||
|
||||
SheetModel {
|
||||
|
Loading…
x
Reference in New Issue
Block a user