use std::path::{Path, PathBuf};
pub(crate) fn quarantine_corrupt(path: &Path) -> Option<PathBuf> {
let secs = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0);
let backup = path.with_extension(format!("json.corrupt-{secs}"));
if std::fs::rename(path, &backup).is_ok() {
return Some(backup);
}
match std::fs::copy(path, &backup) {
Ok(_) => Some(backup),
Err(_) => None,
}
}
pub(crate) fn quarantine_label(path: &Path) -> String {
quarantine_corrupt(path)
.map(|p| p.display().to_string())
.unwrap_or_else(|| "<backup failed>".to_string())
}
pub(crate) fn quarantine_copy(path: &Path) -> Option<PathBuf> {
let secs = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0);
let backup = path.with_extension(format!("json.corrupt-{secs}"));
std::fs::copy(path, &backup).ok().map(|_| backup)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn quarantine_copy_preserves_original_and_backs_up() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("index.json");
std::fs::write(&path, b"original contents").unwrap();
let backup = quarantine_copy(&path).expect("copy should succeed");
assert!(path.exists(), "original must remain in place");
assert_eq!(std::fs::read(&path).unwrap(), b"original contents");
assert!(backup.exists(), "backup copy must exist");
assert_eq!(std::fs::read(&backup).unwrap(), b"original contents");
assert!(backup.to_string_lossy().contains(".corrupt-"));
}
#[test]
fn quarantine_corrupt_moves_original_aside() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("index.json");
std::fs::write(&path, b"corrupt").unwrap();
let backup = quarantine_corrupt(&path).expect("move should succeed");
assert!(!path.exists(), "original must be moved aside");
assert!(backup.exists());
assert_eq!(std::fs::read(&backup).unwrap(), b"corrupt");
}
}