src/cache.rs
Ref: Size: 2.2 KiB
use std::fs;
use std::path::PathBuf;
use git2::{Oid, Repository};
use serde::de::DeserializeOwned;
use serde::Serialize;
/// Return the cache directory path for a given repository.
fn cache_dir(repo: &Repository) -> PathBuf {
repo.path().join("collab").join("cache")
}
/// Sanitize a ref name for use as a filename by replacing `/` with `_`.
fn sanitize_ref_name(ref_name: &str) -> String {
ref_name.replace('/', "_")
}
/// Cache entry stored on disk: the tip OID at cache time + serialized state.
#[derive(serde::Serialize, serde::Deserialize)]
struct CacheEntry {
tip_oid: String,
state: serde_json::Value,
}
/// Try to read a cached state for the given ref.
///
/// Returns `Some(state)` if a cache file exists and its stored tip OID matches
/// the current ref tip. Returns `None` on any mismatch, missing file, or error.
pub fn get_cached_state<T: DeserializeOwned>(
repo: &Repository,
ref_name: &str,
) -> Option<T> {
let current_tip = repo.refname_to_id(ref_name).ok()?;
let path = cache_dir(repo).join(sanitize_ref_name(ref_name));
let data = fs::read_to_string(&path).ok()?;
let entry: CacheEntry = serde_json::from_str(&data).ok()?;
if entry.tip_oid != current_tip.to_string() {
return None;
}
serde_json::from_value(entry.state).ok()
}
/// Write a cached state for the given ref, keyed by the current tip OID.
///
/// Silently ignores errors (cache is best-effort).
pub fn set_cached_state<T: Serialize>(
repo: &Repository,
ref_name: &str,
tip_oid: Oid,
state: &T,
) {
let dir = cache_dir(repo);
if fs::create_dir_all(&dir).is_err() {
return;
}
let entry = CacheEntry {
tip_oid: tip_oid.to_string(),
state: match serde_json::to_value(state) {
Ok(v) => v,
Err(_) => return,
},
};
let json = match serde_json::to_string(&entry) {
Ok(j) => j,
Err(_) => return,
};
let path = dir.join(sanitize_ref_name(ref_name));
let _ = fs::write(&path, json);
}
/// Invalidate (delete) the cache entry for a ref. Best-effort.
pub fn invalidate(repo: &Repository, ref_name: &str) {
let path = cache_dir(repo).join(sanitize_ref_name(ref_name));
let _ = fs::remove_file(path);
}