a73x

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);
}