use std::{fs, path::Path};
const CURRENT_VERSION: &str = "3";
const VERSION_FILE: &str = ".version";
pub fn check_migration(app_dir: &Path) -> anyhow::Result<()> {
let version_path = app_dir.join(VERSION_FILE);
if version_path.exists() {
let version = fs::read_to_string(&version_path)?.trim().to_string();
if version == CURRENT_VERSION {
return Ok(());
}
eprintln!(
"Warning: unknown config version \"{version}\", expected \"{CURRENT_VERSION}\". \
Proceeding anyway."
);
return Ok(());
}
let has_old_data = app_dir.join("registry.toml").exists()
|| app_dir.join("history.toml").exists()
|| app_dir.join("sources.toml").exists();
if has_old_data {
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
let backup_dir = app_dir.with_file_name(format!(".iforgor.bak.{timestamp}"));
fs::rename(app_dir, &backup_dir)?;
eprintln!(
"Old config detected. Backed up to: {}",
backup_dir.display()
);
eprintln!("Migrate your source TOML files to .iforgor/ folders in your projects.");
}
fs::create_dir_all(app_dir)?;
fs::write(app_dir.join(VERSION_FILE), CURRENT_VERSION)?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fresh_directory_creates_version() {
let tmp = tempfile::tempdir().unwrap();
let app_dir = tmp.path().join(".iforgor");
check_migration(&app_dir).unwrap();
let version = fs::read_to_string(app_dir.join(VERSION_FILE)).unwrap();
assert_eq!(version, CURRENT_VERSION);
}
#[test]
fn current_version_is_noop() {
let tmp = tempfile::tempdir().unwrap();
let app_dir = tmp.path().join(".iforgor");
fs::create_dir_all(&app_dir).unwrap();
fs::write(app_dir.join(VERSION_FILE), CURRENT_VERSION).unwrap();
fs::write(app_dir.join("some_file.toml"), "keep me").unwrap();
check_migration(&app_dir).unwrap();
assert!(app_dir.join("some_file.toml").exists());
}
#[test]
fn old_data_triggers_backup() {
let tmp = tempfile::tempdir().unwrap();
let app_dir = tmp.path().join(".iforgor");
fs::create_dir_all(&app_dir).unwrap();
fs::write(app_dir.join("registry.toml"), "old data").unwrap();
check_migration(&app_dir).unwrap();
let version = fs::read_to_string(app_dir.join(VERSION_FILE)).unwrap();
assert_eq!(version, CURRENT_VERSION);
assert!(!app_dir.join("registry.toml").exists());
let backups: Vec<_> = fs::read_dir(tmp.path())
.unwrap()
.filter_map(|e| e.ok())
.filter(|e| e.file_name().to_string_lossy().starts_with(".iforgor.bak."))
.collect();
assert_eq!(backups.len(), 1);
}
#[test]
fn unknown_version_proceeds() {
let tmp = tempfile::tempdir().unwrap();
let app_dir = tmp.path().join(".iforgor");
fs::create_dir_all(&app_dir).unwrap();
fs::write(app_dir.join(VERSION_FILE), "999").unwrap();
check_migration(&app_dir).unwrap();
}
}