use crate::git::GitRepo;
use crate::output::VersionVariables;
use rust_i18n::t;
use sha1::{Digest, Sha1};
use std::path::{Path, PathBuf};
pub fn compute_key(repo: &GitRepo, config_path: Option<&Path>, overrides: &[String]) -> String {
let mut hasher = Sha1::new();
for line in repo.refs_snapshot().unwrap_or_default() {
hasher.update(line.as_bytes());
hasher.update(b"\n");
}
hasher.update(b"--head--");
hasher.update(repo.head_ref_name().as_bytes());
if let Ok(head) = repo.head_commit() {
hasher.update(head.sha.as_bytes());
}
hasher.update(b"--config--");
if let Some(p) = config_path {
if let Ok(content) = std::fs::read_to_string(p) {
hasher.update(content.as_bytes());
}
}
hasher.update(b"--override--");
for o in overrides {
hasher.update(o.as_bytes());
hasher.update(b"\n");
}
let digest = hasher.finalize();
let mut hex = String::with_capacity(40);
for b in digest {
hex.push_str(&format!("{b:02x}"));
}
hex
}
fn cache_file(repo: &GitRepo, key: &str) -> PathBuf {
repo.git_dir()
.join("gitversion_cache")
.join(format!("{key}.json"))
}
pub fn load(repo: &GitRepo, key: &str) -> Option<VersionVariables> {
let path = cache_file(repo, key);
if !path.is_file() {
log::debug!("cache miss: {}", path.display());
return None;
}
match std::fs::read_to_string(&path)
.ok()
.and_then(|s| serde_json::from_str(&s).ok())
{
Some(vars) => {
log::debug!("{}", t!("cache.hit", path = path.display()));
Some(vars)
}
None => {
log::warn!("{}", t!("cache.corrupt", path = path.display()));
let _ = std::fs::remove_file(&path);
None
}
}
}
pub fn store(repo: &GitRepo, key: &str, vars: &VersionVariables) {
let path = cache_file(repo, key);
if let Some(parent) = path.parent() {
if std::fs::create_dir_all(parent).is_err() {
return;
}
}
match serde_json::to_string_pretty(vars) {
Ok(json) => {
if let Err(e) = std::fs::write(&path, json) {
log::warn!(
"{}",
t!("cache.write_failed", path = path.display(), error = e)
);
} else {
log::debug!("cache write: {}", path.display());
}
}
Err(e) => log::warn!("{}", t!("cache.serialize_failed", error = e)),
}
}