diff --git a/crates/relicario-cli/src/main.rs b/crates/relicario-cli/src/main.rs index 370c378..2a27f2a 100644 --- a/crates/relicario-cli/src/main.rs +++ b/crates/relicario-cli/src/main.rs @@ -992,9 +992,32 @@ fn cmd_restore(query: String) -> Result<()> { Ok(()) } -fn cmd_purge(query: String) -> Result<()> { +/// Inner purge: assumes vault is already unlocked and manifest is loaded. +/// Caller is responsible for saving the manifest and committing afterwards. +fn purge_item( + vault: &crate::session::UnlockedVault, + manifest: &mut relicario_core::Manifest, + id: &relicario_core::ItemId, + title: &str, +) -> Result<()> { use std::fs; + let item_path = vault.item_path(id); + if item_path.exists() { fs::remove_file(&item_path)?; } + let att_dir = vault.root().join("attachments").join(id.as_str()); + if att_dir.exists() { fs::remove_dir_all(&att_dir)?; } + manifest.remove(id); + + let _ = crate::helpers::git_command(vault.root(), &["rm", "-rf", "--ignore-unmatch", + &format!("items/{}.enc", id.as_str()), + &format!("attachments/{}", id.as_str()), + ]).status()?; + // Note: caller adds+commits manifest.enc after processing all purges. + eprintln!("Purged: {title}"); + Ok(()) +} + +fn cmd_purge(query: String) -> Result<()> { let vault = crate::session::UnlockedVault::unlock_interactive()?; let mut manifest = vault.load_manifest()?; let entry = resolve_query(&manifest, &query)?; @@ -1002,25 +1025,14 @@ fn cmd_purge(query: String) -> Result<()> { let title = entry.title.clone(); let _ = entry; - // Remove the item file, its attachments directory, and drop the manifest entry. - let item_path = vault.item_path(&id); - if item_path.exists() { fs::remove_file(&item_path)?; } - let att_dir = vault.root().join("attachments").join(id.as_str()); - if att_dir.exists() { fs::remove_dir_all(&att_dir)?; } - manifest.remove(&id); + purge_item(&vault, &mut manifest, &id, &title)?; vault.save_manifest(&manifest)?; - // `git rm -rf --ignore-unmatch` stages the deletions. Then add manifest and commit. - let _ = crate::helpers::git_command(vault.root(), &["rm", "-rf", "--ignore-unmatch", - &format!("items/{}.enc", id.as_str()), - &format!("attachments/{}", id.as_str()), - ]).status()?; let status = crate::helpers::git_command(vault.root(), &["add", "manifest.enc"]).status()?; if !status.success() { anyhow::bail!("git add manifest.enc failed"); } let status = crate::helpers::git_command(vault.root(), &["commit", "-m", &format!("purge: {} ({})", title, id.as_str())]).status()?; if !status.success() { anyhow::bail!("git commit failed"); } - eprintln!("Purged: {title}"); Ok(()) } @@ -1033,8 +1045,9 @@ fn cmd_trash(action: TrashAction) -> Result<()> { fn cmd_trash_empty() -> Result<()> { use relicario_core::time::now_unix; + let vault = crate::session::UnlockedVault::unlock_interactive()?; - let manifest = vault.load_manifest()?; + let mut manifest = vault.load_manifest()?; let settings = vault.load_settings()?; let now = now_unix(); @@ -1051,10 +1064,20 @@ fn cmd_trash_empty() -> Result<()> { return Ok(()); } + let mut purged_titles = Vec::new(); for (id, title) in purgeable { - cmd_purge(id.as_str().to_string())?; - eprintln!(" purged {title}"); + purge_item(&vault, &mut manifest, &id, &title)?; + purged_titles.push(title); } + + vault.save_manifest(&manifest)?; + let status = crate::helpers::git_command(vault.root(), &["add", "manifest.enc"]).status()?; + if !status.success() { anyhow::bail!("git add manifest.enc failed"); } + let status = crate::helpers::git_command(vault.root(), + &["commit", "-m", &format!("trash empty: purged {} item(s)", purged_titles.len())]).status()?; + if !status.success() { anyhow::bail!("git commit failed"); } + + eprintln!("Emptied trash: {} item(s)", purged_titles.len()); Ok(()) } fn cmd_attach(_q: String, _file: PathBuf) -> Result<()> { bail!("not yet implemented"); }