Add PDF archive store, raw-body IPC and export file helpers
The searchable PDF can be archived content-addressed under the local data dir with its sha256 and layout fingerprint; a different PDF for an archived invoice is refused and reads verify the hash. Export writes go through a raw-body command that remembers the last folder; reveal-in-folder and open fall back to xdg-open / explorer. Adds a Windows-safe export file name builder. Migration M4 adds the archive columns and last export dir.
This commit is contained in:
+30
-2
@@ -10,7 +10,7 @@ const MAX_BACKUPS: usize = 10;
|
||||
const BACKUP_PREFIX: &str = "voiced-pre-v";
|
||||
|
||||
/// Highest schema version, i.e. the number of entries in `migrations()`.
|
||||
const LATEST_VERSION: i64 = 3;
|
||||
const LATEST_VERSION: i64 = 4;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum DbError {
|
||||
@@ -174,8 +174,15 @@ const M3: &str = r#"
|
||||
ALTER TABLE app_settings ADD COLUMN render_prefs TEXT NOT NULL DEFAULT '';
|
||||
"#;
|
||||
|
||||
/// Version 4: the archived PDF's layout fingerprint and time, and the last export folder.
|
||||
const M4: &str = r#"
|
||||
ALTER TABLE invoices ADD COLUMN archived_fingerprint TEXT;
|
||||
ALTER TABLE invoices ADD COLUMN archived_at TEXT;
|
||||
ALTER TABLE app_settings ADD COLUMN last_export_dir TEXT NOT NULL DEFAULT '';
|
||||
"#;
|
||||
|
||||
fn migrations() -> Migrations<'static> {
|
||||
Migrations::new(vec![M::up(SCHEMA), M::up(M2), M::up(M3)])
|
||||
Migrations::new(vec![M::up(SCHEMA), M::up(M2), M::up(M3), M::up(M4)])
|
||||
}
|
||||
|
||||
/// Open (creating if needed) the database at `path` and bring it to the latest schema.
|
||||
@@ -541,6 +548,27 @@ CREATE INDEX IF NOT EXISTS idx_invoices_created ON invoices(created_at DESC);
|
||||
assert!(backups_in(&backups).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn m4_adds_archive_and_export_dir_columns() {
|
||||
let conn = open_in_memory().unwrap();
|
||||
let dir: String = conn
|
||||
.query_row("SELECT last_export_dir FROM app_settings WHERE id = 1", [], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(dir, "");
|
||||
// The columns exist and are nullable on invoices (no rows needed to prove it).
|
||||
conn.prepare("SELECT archived_pdf_sha256, archived_fingerprint, archived_at FROM invoices")
|
||||
.unwrap();
|
||||
let notnull: i64 = conn
|
||||
.query_row(
|
||||
"SELECT SUM(\"notnull\") FROM pragma_table_info('invoices')
|
||||
WHERE name IN ('archived_fingerprint', 'archived_at')",
|
||||
[],
|
||||
|r| r.get(0),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(notnull, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn render_prefs_column_defaults_to_empty_and_round_trips() {
|
||||
let conn = open_in_memory().unwrap();
|
||||
|
||||
Reference in New Issue
Block a user