use std::io::{Error, ErrorKind, Result as IoResult};
fn read_git_file(file_path: String) -> IoResult<String> {
let string = std::fs::read_to_string(&file_path)?;
println!("cargo:rerun-if-changed={file_path}");
Ok(string)
}
fn git_path(path: &str) -> IoResult<String> {
let output = std::process::Command::new("git")
.args(["rev-parse", "--git-path", path])
.output()
.map_err(|e| Error::new(e.kind(), "failed to run `git`"))?;
if !output.status.success() {
return Err(Error::new(
ErrorKind::Other,
format!(
"`git` failed with status {}: {}",
output.status,
std::str::from_utf8(&output.stderr).unwrap_or("no output")
),
));
}
let output = std::str::from_utf8(&output.stdout)
.map_err(|_e| Error::new(ErrorKind::InvalidData, "`git` output was not utf-8"))?
.trim();
if output.is_empty() {
Err(Error::new(ErrorKind::InvalidData, "`git` output was empty"))
} else {
Ok(output.to_owned())
}
}
fn embed_commit_hash() -> Result<(), (Error, &'static str)> {
let head_path = git_path("HEAD").map_err(|e| (e, "failed to get HEAD path"))?;
let head_contents = read_git_file(head_path).map_err(|e| (e, "failed to read HEAD"))?;
let commit = if let Some(ref_path) = head_contents.strip_prefix("ref: ") {
let ref_path = git_path(ref_path).map_err(|e| (e, "failed to get ref path"))?;
read_git_file(ref_path).map_err(|e| (e, "failed to read ref"))?
} else {
head_contents
};
println!("cargo:rustc-env=GIT_COMMIT_HASH={commit}");
Ok(())
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
if let Err((err, details)) = embed_commit_hash() {
println!("cargo:warning={details}: {err}");
}
Ok(())
}