diff --git a/crates/ipx/src/retention.rs b/crates/ipx/src/retention.rs index a857b3f..706ae7a 100644 --- a/crates/ipx/src/retention.rs +++ b/crates/ipx/src/retention.rs @@ -45,9 +45,17 @@ pub fn aged(candidates: &[Candidate], cutoff: i64) -> Vec { pub async fn run(cfg: &Config, db: &Db, dry_run: bool) -> Result { let mut report = Report::default(); - // Someone may have deleted a file by hand; the row must stop claiming it exists. + // Someone may have deleted a file by hand; the row must stop claiming it exists. Not while + // the folder itself is gone (the array stopped, shfs dropped): every file would look deleted + // and the whole library be forgotten at once (#293). // Each step is a span of the reap's trace, so a slow one says which part was slow. - for (id, path) in db.missing_files().instrument(step("missing_files")).await? { + let missing = if cfg.general.download_dir.is_dir() { + db.missing_files().instrument(step("missing_files")).await? + } else { + tracing::warn!(dir = %cfg.general.download_dir.display(), "the downloads folder is not there; left every file as it was"); + vec![] + }; + for (id, path) in missing { if !dry_run { db.mark_reaped(id).await?; } @@ -204,6 +212,23 @@ mod tests { assert_eq!(db.prune_entries(500).await.unwrap(), 1, "the old item goes, a leaver's pin or not"); } + #[tokio::test] + async fn a_downloads_folder_that_is_gone_forgets_no_file() { + let db = Db::memory().await.unwrap(); + db.exec_for_test( + "INSERT INTO enclosures (id, feed_id, guid, url, path, state) VALUES + (1, 'f', 'a', 'u1', '/nowhere/ipx-test/a.mp3', 'done');", + ) + .await + .unwrap(); + let mut cfg = Config::default(); + cfg.general.download_dir = "/nowhere/ipx-test".into(); + assert_eq!(run(&cfg, &db, false).await.unwrap().reconciled, 0, "the folder is gone, not the file"); + + cfg.general.download_dir = std::env::temp_dir(); + assert_eq!(run(&cfg, &db, false).await.unwrap().reconciled, 1, "the folder is there and the file is not"); + } + #[tokio::test] async fn prune_keeps_entries_that_still_have_a_file() { let db = Db::memory().await.unwrap();