Skip to content
//! Tiny throwaway temporary-directory helper for tests.
//!
//! Avoids pulling in an external crate. Creates a uniquely-named directory
//! under the system temp dir and removes it (recursively) on drop.

use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicU64, Ordering};

static COUNTER: AtomicU64 = AtomicU64::new(0);

/// A temporary directory that is deleted when dropped.
pub struct TempDir {
    path: PathBuf,
}

impl TempDir {
    /// Create a new uniquely-named temporary directory.
    pub fn new(prefix: &str) -> Self {
        let base = std::env::temp_dir();
        let pid = std::process::id();
        let n = COUNTER.fetch_add(1, Ordering::Relaxed);
        let nanos = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .map_or(0, |d| d.as_nanos());
        let path = base.join(format!("{prefix}-{pid}-{nanos}-{n}"));
        std::fs::create_dir_all(&path).expect("failed to create temp dir");
        Self { path }
    }

    /// The path to the temporary directory.
    pub fn path(&self) -> &Path {
        &self.path
    }
}

impl Drop for TempDir {
    fn drop(&mut self) {
        let _ = std::fs::remove_dir_all(&self.path);
    }
}

/// Run a git command in `dir`, panicking on failure. Test-only helper.
#[cfg(test)]
fn git(dir: &Path, args: &[&str]) {
    use std::process::Command;
    let status = Command::new("git")
        .args(args)
        .current_dir(dir)
        .env("GIT_AUTHOR_NAME", "Tester")
        .env("GIT_AUTHOR_EMAIL", "tester@example.com")
        .env("GIT_COMMITTER_NAME", "Tester")
        .env("GIT_COMMITTER_EMAIL", "tester@example.com")
        .status()
        .expect("failed to spawn git");
    assert!(status.success(), "git {args:?} failed");
}

/// Build a small repository with two tags (`v1`, `v2`) and two commits between
/// them, returning the owning `TempDir`. Test-only helper shared across modules.
#[cfg(test)]
pub fn make_git_repo_with_tags() -> TempDir {
    let dir = TempDir::new("eris-git-test");
    let p = dir.path();
    git(p, &["init", "-q"]);
    git(p, &["config", "commit.gpgsign", "false"]);

    std::fs::write(p.join("a.txt"), "hello\n").unwrap();
    git(p, &["add", "."]);
    git(p, &["commit", "-q", "-m", "initial commit"]);
    git(p, &["tag", "v1"]);

    std::fs::write(p.join("a.txt"), "hello world\n").unwrap();
    git(p, &["add", "."]);
    git(
        p,
        &[
            "commit",
            "-q",
            "-m",
            "feat: add greeting\n\nMore detailed body text.",
        ],
    );

    std::fs::write(p.join("b.txt"), "second\n").unwrap();
    git(p, &["add", "."]);
    git(p, &["commit", "-q", "-m", "fix: correct typo"]);
    git(p, &["tag", "v2"]);

    dir
}