use std::collections::hash_map::DefaultHasher;
use std::fs;
use std::hash::{Hash, Hasher};
use std::path::PathBuf;
use anyhow::Result;
use git2::Repository;
use serde::{Deserialize, Serialize};
use crate::insights::ReviewPack;
const INDEX_VERSION: u8 = 1;
#[derive(Debug, Clone, Serialize, Deserialize)]
struct ReviewPackCacheEntry {
version: u8,
repo_id: String,
head: String,
selected_hash: Option<String>,
pack: ReviewPack,
}
fn repo_id() -> Option<String> {
let repo = Repository::discover(".").ok()?;
let root = repo
.workdir()
.or_else(|| repo.path().parent())
.unwrap_or(repo.path());
let mut hasher = DefaultHasher::new();
root.to_string_lossy().hash(&mut hasher);
Some(format!("{:016x}", hasher.finish()))
}
fn current_head() -> Option<String> {
let repo = Repository::discover(".").ok()?;
let oid = repo.head().ok()?.target()?;
Some(oid.to_string())
}
fn cache_dir() -> Option<PathBuf> {
dirs::cache_dir().map(|p| p.join("gitstack").join("index-v1"))
}
fn review_pack_cache_path(selected_hash: Option<&str>) -> Option<PathBuf> {
let rid = repo_id()?;
let suffix = selected_hash.unwrap_or("none");
let file_name = format!("review-pack-{}-{}.json", rid, suffix);
Some(cache_dir()?.join(file_name))
}
pub fn load_review_pack(selected_hash: Option<&str>) -> Option<ReviewPack> {
let path = review_pack_cache_path(selected_hash)?;
let content = fs::read_to_string(path).ok()?;
let entry: ReviewPackCacheEntry = serde_json::from_str(&content).ok()?;
if entry.version != INDEX_VERSION {
return None;
}
if entry.repo_id != repo_id()? {
return None;
}
if entry.head != current_head()? {
return None;
}
if entry.selected_hash.as_deref() != selected_hash {
return None;
}
Some(entry.pack)
}
pub fn save_review_pack(selected_hash: Option<&str>, pack: &ReviewPack) -> Result<()> {
let Some(path) = review_pack_cache_path(selected_hash) else {
return Ok(());
};
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?;
}
let Some(repo_id) = repo_id() else {
return Ok(());
};
let Some(head) = current_head() else {
return Ok(());
};
let entry = ReviewPackCacheEntry {
version: INDEX_VERSION,
repo_id,
head,
selected_hash: selected_hash.map(str::to_string),
pack: pack.clone(),
};
let json = serde_json::to_string_pretty(&entry)?;
fs::write(path, json)?;
Ok(())
}