use anyhow::Result; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::fs; use std::path::{Path, PathBuf}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CacheManifest { #[serde(default)] pub git: HashMap, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct GitCacheEntry { pub path: PathBuf, pub resolved_ref: String, pub fetched_at: String, } impl CacheManifest { pub fn load(cache_dir: &Path) -> Result { let manifest_path = cache_dir.join("cache.json"); if !manifest_path.exists() { return Ok(Self { git: HashMap::new(), }); } let content = fs::read_to_string(&manifest_path)?; let manifest = serde_json::from_str(&content)?; Ok(manifest) } pub fn save(&self, cache_dir: &Path) -> Result<()> { if !cache_dir.exists() { fs::create_dir_all(cache_dir)?; } let manifest_path = cache_dir.join("cache.json"); let content = serde_json::to_string_pretty(self)?; fs::write(manifest_path, content)?; Ok(()) } } pub fn get_cache_root(project_root: &Path) -> PathBuf { project_root.join("cache") } pub fn get_git_worktree_path(project_root: &Path, repo_url: &str) -> PathBuf { let cache_root = get_cache_root(project_root); let id = normalized_repo_id(repo_url); cache_root.join("git").join(id).join("worktree") } fn normalized_repo_id(url: &str) -> String { let mut hash = 0xcbf29ce484222325; for b in url.as_bytes() { hash ^= *b as u64; hash = hash.wrapping_mul(0x100000001b3); } format!("{:016x}", hash) }