use std::path::{Path, PathBuf};
use super::distributed::{
detect_legacy_lockfile, meta_lockfile_path, read_meta_lockfile, write_meta_lockfile,
};
use super::entry::LockfileError;
#[non_exhaustive]
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct MigrationReport {
pub lockfile: PathBuf,
pub migrated_entries: usize,
pub already_migrated: bool,
pub no_lockfile: bool,
}
pub fn migrate_v1_1_1_lockfile(root_meta: &Path) -> Result<MigrationReport, LockfileError> {
let lockfile = meta_lockfile_path(root_meta);
if !lockfile.exists() {
return Ok(MigrationReport {
lockfile,
migrated_entries: 0,
already_migrated: false,
no_lockfile: true,
});
}
if !detect_legacy_lockfile(root_meta)? {
return Ok(MigrationReport {
lockfile,
migrated_entries: 0,
already_migrated: true,
no_lockfile: false,
});
}
let entries = read_meta_lockfile(root_meta)?;
let migrated_entries = entries.len();
write_meta_lockfile(root_meta, &entries)?;
Ok(MigrationReport { lockfile, migrated_entries, already_migrated: false, no_lockfile: false })
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::tempdir;
#[test]
fn test_migrator_v1_1_1_to_v1_2_0() {
let dir = tempdir().unwrap();
let lock_dir = dir.path().join(".grex");
std::fs::create_dir_all(&lock_dir).unwrap();
let v1_1_1 = "\
{\"id\":\"alpha\",\"sha\":\"abc\",\"branch\":\"main\",\"installed_at\":\"2026-04-27T10:00:00Z\",\"actions_hash\":\"h\",\"schema_version\":\"1\"}
{\"id\":\"beta\",\"sha\":\"def\",\"branch\":\"main\",\"installed_at\":\"2026-04-27T10:00:00Z\",\"actions_hash\":\"h\",\"schema_version\":\"1\"}
";
std::fs::write(lock_dir.join("grex.lock.jsonl"), v1_1_1).unwrap();
let report = migrate_v1_1_1_lockfile(dir.path()).expect("migrate");
assert_eq!(report.migrated_entries, 2);
assert!(!report.already_migrated);
assert!(!report.no_lockfile);
let raw = std::fs::read_to_string(lock_dir.join("grex.lock.jsonl")).unwrap();
assert!(raw.contains("\"path\":\"alpha\""), "got: {raw}");
assert!(raw.contains("\"path\":\"beta\""), "got: {raw}");
assert!(!detect_legacy_lockfile(dir.path()).unwrap());
}
#[test]
fn test_migrator_idempotent() {
let dir = tempdir().unwrap();
let lock_dir = dir.path().join(".grex");
std::fs::create_dir_all(&lock_dir).unwrap();
let v1_1_1 = "{\"id\":\"alpha\",\"sha\":\"abc\",\"branch\":\"main\",\"installed_at\":\"2026-04-27T10:00:00Z\",\"actions_hash\":\"h\",\"schema_version\":\"1\"}\n";
std::fs::write(lock_dir.join("grex.lock.jsonl"), v1_1_1).unwrap();
let first = migrate_v1_1_1_lockfile(dir.path()).unwrap();
assert_eq!(first.migrated_entries, 1);
assert!(!first.already_migrated);
let bytes_after_first = std::fs::read(lock_dir.join("grex.lock.jsonl")).unwrap();
let second = migrate_v1_1_1_lockfile(dir.path()).unwrap();
assert_eq!(second.migrated_entries, 0);
assert!(second.already_migrated);
let bytes_after_second = std::fs::read(lock_dir.join("grex.lock.jsonl")).unwrap();
assert_eq!(bytes_after_first, bytes_after_second, "second run must not rewrite the file",);
}
#[test]
fn test_migrator_no_lockfile_is_noop() {
let dir = tempdir().unwrap();
let report = migrate_v1_1_1_lockfile(dir.path()).unwrap();
assert_eq!(report.migrated_entries, 0);
assert!(report.no_lockfile);
assert!(!report.already_migrated);
assert!(!meta_lockfile_path(dir.path()).exists());
}
}