use anyhow::{Context, Result};
use std::fs;
use std::path::Path;
pub fn migrate_file<F>(path: &Path, old_version: u32, extension: &str, save_fn: F) -> Result<()>
where
F: FnOnce() -> Result<()>,
{
let backup_path = path.with_extension(format!("{}.backup-v{}", extension, old_version));
fs::copy(path, &backup_path).with_context(|| {
format!(
"Failed to create backup at {} before migration",
backup_path.display()
)
})?;
save_fn().with_context(|| format!("Failed to save migrated file to {}", path.display()))?;
let _ = fs::remove_file(&backup_path);
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
#[test]
fn test_migrate_file_creates_and_removes_backup() {
let temp_dir = TempDir::new().unwrap();
let file_path = temp_dir.path().join("config.toml");
fs::write(&file_path, "version = 0\nname = \"test\"").unwrap();
let result = migrate_file(&file_path, 0, "toml", || {
fs::write(&file_path, "version = 1\nname = \"test\"")?;
Ok(())
});
assert!(result.is_ok());
let backup_path = file_path.with_extension("toml.backup-v0");
assert!(!backup_path.exists(), "Backup should be removed on success");
let content = fs::read_to_string(&file_path).unwrap();
assert!(content.contains("version = 1"));
}
#[test]
fn test_migrate_file_keeps_backup_on_failure() {
let temp_dir = TempDir::new().unwrap();
let file_path = temp_dir.path().join("config.toml");
fs::write(&file_path, "version = 0\nname = \"test\"").unwrap();
let result = migrate_file(&file_path, 0, "toml", || {
anyhow::bail!("Simulated migration failure")
});
assert!(result.is_err());
let backup_path = file_path.with_extension("toml.backup-v0");
assert!(backup_path.exists(), "Backup should remain on failure");
let backup_content = fs::read_to_string(&backup_path).unwrap();
assert!(backup_content.contains("version = 0"));
}
}