//! Minimal `git` CLI wrappers.
//!
//! Two functions: [`capture`] for subcommands whose stdout we need to parse,
//! and [`run`] for subcommands whose status we just care about. Both shell
//! out to the system `git` binary via `-C <cwd>` rather than chdir'ing.
//! safe under concurrent callers.
//!
//! Argument values are passed as array elements (never interpolated into a
//! shell string), so shell-metacharacters in inputs cannot inject commands.

use std::path::Path;
use std::process::Command;

use anyhow::{Context, Result};

/// Runs `git -C <cwd> <args…>` and returns stdout as a UTF-8 string.
///
/// # Errors
///
/// Returns an error if the process fails to spawn, exits non-zero
/// (`stderr` is included in the message), or produces non-UTF-8 output.
pub fn capture(cwd: &Path, args: &[&str]) -> Result<String> {
    let out = Command::new("git")
        .arg("-C")
        .arg(cwd)
        .args(args)
        .output()
        .with_context(|| format!("spawn git {}", args.join(" ")))?;
    if !out.status.success() {
        anyhow::bail!(
            "git {} failed in {}: {}",
            args.join(" "),
            cwd.display(),
            String::from_utf8_lossy(&out.stderr).trim()
        );
    }
    String::from_utf8(out.stdout).context("git stdout was not utf-8")
}

/// Runs `git -C <cwd> <args…>` and returns `Ok(())` on success.
///
/// Output streams are inherited from the caller. Git prints directly to
/// the user's terminal. Prefer [`capture`] when you need to parse stdout.
///
/// # Errors
///
/// Returns an error if the process fails to spawn or exits non-zero.
pub fn run(cwd: &Path, args: &[&str]) -> Result<()> {
    let status = Command::new("git")
        .arg("-C")
        .arg(cwd)
        .args(args)
        .status()
        .with_context(|| format!("spawn git {}", args.join(" ")))?;
    if !status.success() {
        anyhow::bail!("git {} failed in {}", args.join(" "), cwd.display());
    }
    Ok(())
}
