use std::fs;
use std::io::{BufReader, BufWriter, Write};
use std::path::{Path, PathBuf};
use std::process::Command;
use unfault_core::types::graph_query::CallersContext;
use xxhash_rust::xxh3::xxh3_64;
const CACHE_VERSION: u8 = 1;
pub fn current_commit_sha(workspace_path: &Path) -> String {
Command::new("git")
.args(["rev-parse", "--short=12", "HEAD"])
.current_dir(workspace_path)
.output()
.ok()
.and_then(|o| {
if o.status.success() {
String::from_utf8(o.stdout)
.ok()
.map(|s| s.trim().to_string())
} else {
None
}
})
.unwrap_or_else(|| "no-git".to_string())
}
pub fn query_cache_dir(workspace_path: &Path) -> PathBuf {
workspace_path.join(".unfault").join("cache").join("query")
}
fn cache_path(
workspace_path: &Path,
function_name: &str,
file_hint: Option<&str>,
commit_sha: &str,
) -> PathBuf {
let key = format!("{}|{}", function_name, file_hint.unwrap_or(""));
let key_hash = xxh3_64(key.as_bytes());
let filename = format!(
"v{}_callers_{:016x}_{}.msgpack",
CACHE_VERSION, key_hash, commit_sha
);
query_cache_dir(workspace_path).join(filename)
}
pub fn get_callers(
workspace_path: &Path,
function_name: &str,
file_hint: Option<&str>,
commit_sha: &str,
) -> Option<CallersContext> {
let path = cache_path(workspace_path, function_name, file_hint, commit_sha);
let file = fs::File::open(&path).ok()?;
rmp_serde::from_read(BufReader::new(file)).ok()
}
pub fn set_callers(
workspace_path: &Path,
function_name: &str,
file_hint: Option<&str>,
commit_sha: &str,
ctx: &CallersContext,
) {
let dir = query_cache_dir(workspace_path);
if fs::create_dir_all(&dir).is_err() {
return;
}
let path = cache_path(workspace_path, function_name, file_hint, commit_sha);
if let Ok(file) = fs::File::create(&path) {
let mut writer = BufWriter::new(file);
let _ = rmp_serde::encode::write(&mut writer, ctx);
let _ = writer.flush();
}
}
pub fn clear(workspace_path: &Path) -> std::io::Result<()> {
let dir = query_cache_dir(workspace_path);
if !dir.exists() {
return Ok(());
}
for entry in fs::read_dir(&dir)? {
let entry = entry?;
if entry.path().extension().map_or(false, |e| e == "msgpack") {
fs::remove_file(entry.path())?;
}
}
Ok(())
}