migrate-guard 0.1.1

Detect and auto-renumber colliding sqlx migration version numbers across git branches.
Documentation
use migrate_guard::apply::{apply, ApplyMode};
use migrate_guard::config::Config;
use std::path::Path;
use std::process::Command;

fn git(repo: &Path, args: &[&str]) {
    assert!(Command::new("git")
        .args(args)
        .current_dir(repo)
        .status()
        .unwrap()
        .success());
}
fn write(repo: &Path, rel: &str, body: &str) {
    let p = repo.join(rel);
    std::fs::create_dir_all(p.parent().unwrap()).unwrap();
    std::fs::write(p, body).unwrap();
}

#[test]
fn renumbers_topic_branch_above_max_and_rewrites_reference() {
    let tmp = tempfile::tempdir().unwrap();
    let repo = tmp.path();
    git(repo, &["init", "-q", "-b", "main"]);
    git(repo, &["config", "user.email", "t@t.test"]);
    git(repo, &["config", "user.name", "t"]);

    write(repo, "migrations/0001_a.sql", "");
    write(
        repo,
        "migrate-guard.toml",
        "base_ref=\"main\"\n[[dir]]\nrole=\"platform\"\npath=\"migrations\"\n",
    );
    git(repo, &["add", "-A"]);
    git(repo, &["commit", "-qm", "base"]);

    git(repo, &["checkout", "-qb", "other"]);
    write(repo, "migrations/0002_other.sql", "");
    git(repo, &["add", "-A"]);
    git(repo, &["commit", "-qm", "other"]);

    git(repo, &["checkout", "-q", "main"]);
    git(repo, &["checkout", "-qb", "feat"]);
    write(repo, "migrations/0002_mine.sql", "");
    write(
        repo,
        "src/lib.rs",
        "const M: &str = include_str!(\"../migrations/0002_mine.sql\");\n",
    );
    git(repo, &["add", "-A"]);
    git(repo, &["commit", "-qm", "feat"]);

    let cfg = Config::load(repo).unwrap();

    let dry = apply(repo, &cfg, "feat", ApplyMode::DryRun).unwrap();
    assert_eq!(dry[0].renames.len(), 1);
    assert_eq!(dry[0].renames[0].new_version, 3);
    assert_eq!(dry[0].reference_hits.len(), 1);
    assert!(
        repo.join("migrations/0002_mine.sql").exists(),
        "dry run must not move files"
    );

    apply(repo, &cfg, "feat", ApplyMode::Commit).unwrap();
    assert!(repo.join("migrations/0003_mine.sql").exists());
    assert!(!repo.join("migrations/0002_mine.sql").exists());
    let src = std::fs::read_to_string(repo.join("src/lib.rs")).unwrap();
    assert!(src.contains("0003_mine.sql") && !src.contains("0002_mine.sql"));

    let again = apply(repo, &cfg, "feat", ApplyMode::DryRun).unwrap();
    assert!(again[0].renames.is_empty());
}