diff --git a/src-tauri/src/compress.rs b/src-tauri/src/compress.rs index 502aa8b..db1a232 100644 --- a/src-tauri/src/compress.rs +++ b/src-tauri/src/compress.rs @@ -93,7 +93,20 @@ pub async fn compress_files( match run_gs(&app, args).await { Ok(()) => { - std::fs::rename(&tmp_path, &output_path).map_err(|e| e.to_string())?; + if let Err(e) = std::fs::rename(&tmp_path, &output_path) { + let _ = std::fs::remove_file(&tmp_path); + let _ = app.emit( + "compress:progress", + ProgressEvent { + file: job.path.clone(), + status: "error".into(), + saved_bytes: None, + compressed_size: None, + error_msg: Some(e.to_string()), + }, + ); + continue; + } let compressed_size = std::fs::metadata(&output_path) .map(|m| m.len() as i64) diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 6e7a6b9..20a8bde 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -45,11 +45,6 @@ pub fn check_path_writable(path: String) -> bool { } } -#[tauri::command] -fn check_path_writable_cmd(path: String) -> bool { - check_path_writable(path) -} - #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { use crate::compress::compress_files; @@ -89,7 +84,6 @@ pub fn run() { reveal_in_finder, get_file_meta, validate_pdf, - check_path_writable_cmd, set_menu_item_enabled, check_for_update, ]) diff --git a/src-tauri/src/menu.rs b/src-tauri/src/menu.rs index e466a3e..d30a2ca 100644 --- a/src-tauri/src/menu.rs +++ b/src-tauri/src/menu.rs @@ -175,5 +175,50 @@ mod tests { assert!(ids.contains("clear-queue")); assert!(ids.contains("compress")); assert!(ids.contains("reset-selected")); + assert!(ids.contains("check-for-update")); + } + + #[test] + fn menu_ids_sync_with_build_menu() { + // Note: build_menu requires AppHandle and cannot be called in unit tests. + // Instead we maintain a parallel list here that must be kept in sync with + // the map.insert calls in build_menu. + // Hardcoded list of IDs that are inserted in build_menu's HashMap + let build_menu_ids: &[&str] = &[ + "add-files", + "reveal-in-finder", + "clear-queue", + "compress", + "reset-selected", + "check-for-update", + ]; + + // Assert length matches + assert_eq!( + MENU_IDS.len(), + build_menu_ids.len(), + "MENU_IDS and build_menu HashMap have different lengths" + ); + + // Assert all MENU_IDS appear in build_menu_ids + let build_menu_set: std::collections::HashSet<&str> = + build_menu_ids.iter().copied().collect(); + for id in MENU_IDS { + assert!( + build_menu_set.contains(id), + "ID '{}' in MENU_IDS is not inserted in build_menu", + id + ); + } + + // Assert all build_menu_ids appear in MENU_IDS + let menu_ids_set: std::collections::HashSet<&str> = MENU_IDS.iter().copied().collect(); + for id in build_menu_ids { + assert!( + menu_ids_set.contains(id), + "ID '{}' inserted in build_menu is not in MENU_IDS", + id + ); + } } } diff --git a/src-tauri/src/updater.rs b/src-tauri/src/updater.rs index 37ae6ef..b3f4c6d 100644 --- a/src-tauri/src/updater.rs +++ b/src-tauri/src/updater.rs @@ -1,5 +1,9 @@ +fn strip_v(tag: &str) -> &str { + tag.strip_prefix('v').unwrap_or(tag) +} + pub fn parse_version(tag: &str) -> Option<(u64, u64, u64)> { - let s = tag.strip_prefix('v').unwrap_or(tag); + let s = strip_v(tag); let parts: Vec<&str> = s.split('.').collect(); if parts.len() != 3 { return None; @@ -40,13 +44,7 @@ pub async fn check_for_update() -> Option { .ok()?; if is_newer(&release.tag_name, env!("CARGO_PKG_VERSION")) { - Some( - release - .tag_name - .strip_prefix('v') - .unwrap_or(&release.tag_name) - .to_string(), - ) + Some(strip_v(&release.tag_name).to_string()) } else { None } diff --git a/src/lib/components/ActionBar.svelte b/src/lib/components/ActionBar.svelte index 9b50aef..839e6c3 100644 --- a/src/lib/components/ActionBar.svelte +++ b/src/lib/components/ActionBar.svelte @@ -8,6 +8,7 @@ import { selectedFileId } from "$lib/stores/selectionStore"; import { settings } from "$lib/stores/settingsStore"; import { toast } from "$lib/stores/toastStore"; + import { basename } from "$lib/fileActions"; import { buildNotificationBody } from "$lib/notification"; const doneCount = derived(queue, ($q) => $q.filter((e) => e.status === "done").length); @@ -59,7 +60,7 @@ compressDone++; } else if (payload.status === "error") { queue.updateStatus(payload.file, "error", { errorMsg: payload.error_msg }); - const name = payload.file.split("/").pop() ?? payload.file; + const name = basename(payload.file); toast.show(`${name}: ${payload.error_msg ?? "Compression failed"}`); compressDone++; } else { diff --git a/src/lib/components/DetailPanel.svelte b/src/lib/components/DetailPanel.svelte index 3b961bb..e2a6259 100644 --- a/src/lib/components/DetailPanel.svelte +++ b/src/lib/components/DetailPanel.svelte @@ -1,29 +1,20 @@