use std::io;
use std::path::{Path, PathBuf};
use std::process::Command;
pub trait GitRunner {
fn run(&self, dest: &Path, args: &[&str]) -> io::Result<()>;
fn run_capture(&self, dest: &Path, args: &[&str]) -> io::Result<String>;
fn run_capture_bytes(&self, dest: &Path, args: &[&str]) -> io::Result<Vec<u8>> {
self.run_capture(dest, args).map(String::into_bytes)
}
}
pub struct RealGit {
pub(super) bin: PathBuf,
}
impl RealGit {
pub fn new() -> Self {
Self {
bin: PathBuf::from("git"),
}
}
}
impl Default for RealGit {
fn default() -> Self {
Self::new()
}
}
impl GitRunner for RealGit {
fn run(&self, dest: &Path, args: &[&str]) -> io::Result<()> {
self.run_capture_bytes(dest, args).map(|_| ())
}
fn run_capture(&self, dest: &Path, args: &[&str]) -> io::Result<String> {
self.run_capture_bytes(dest, args)
.map(|out| String::from_utf8_lossy(&out).trim().to_string())
}
fn run_capture_bytes(&self, dest: &Path, args: &[&str]) -> io::Result<Vec<u8>> {
let mut cmd = Command::new(&self.bin);
for var in INHERITED_GIT_ENV {
cmd.env_remove(var);
}
let out = cmd.arg("-C").arg(dest).args(args).output()?;
if !out.status.success() {
return Err(io::Error::other(format!(
"git {args:?} exited with {}: {}",
out.status,
String::from_utf8_lossy(&out.stderr).trim()
)));
}
Ok(out.stdout)
}
}
const INHERITED_GIT_ENV: &[&str] = &[
"GIT_DIR",
"GIT_WORK_TREE",
"GIT_INDEX_FILE",
"GIT_OBJECT_DIRECTORY",
"GIT_PREFIX",
"GIT_COMMON_DIR",
"GIT_ALTERNATE_OBJECT_DIRECTORIES",
];