use std::collections::{BTreeSet, HashMap};
use std::path::{Path, PathBuf};
use crate::config::BranchFilter;
use crate::git::graph::{DiffStat, GraphFilters, GraphRow};
pub(crate) const GRAPH_CACHE_CAPACITY: usize = 32;
pub(crate) const RENDER_CACHE_CAPACITY: usize = 256;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub(crate) struct GraphCacheKey {
pub(crate) path: PathBuf,
pub(crate) branch_filter: BranchFilter,
pub(crate) first_parent: bool,
pub(crate) show_stats: bool,
pub(crate) filters: GraphFilters,
}
#[derive(Clone)]
pub(crate) struct CachedGraph {
pub(crate) rows: Vec<GraphRow>,
pub(crate) filter_branches: BTreeSet<String>,
pub(crate) filter_authors: BTreeSet<String>,
}
pub(crate) struct GraphCache {
entries: HashMap<GraphCacheKey, GraphCacheEntry>,
capacity: usize,
clock: u64,
}
struct GraphCacheEntry {
cached: CachedGraph,
last_used: u64,
}
impl GraphCache {
pub(crate) fn new(capacity: usize) -> Self {
Self {
entries: HashMap::new(),
capacity,
clock: 0,
}
}
pub(crate) fn get(&mut self, key: &GraphCacheKey) -> Option<CachedGraph> {
let entry = self.entries.get_mut(key)?;
self.clock += 1;
entry.last_used = self.clock;
Some(entry.cached.clone())
}
#[cfg(test)]
pub(crate) fn contains(&self, key: &GraphCacheKey) -> bool {
self.entries.contains_key(key)
}
pub(crate) fn insert(&mut self, key: GraphCacheKey, cached: CachedGraph) {
if self.entries.len() >= self.capacity && !self.entries.contains_key(&key) {
self.evict_lru();
}
self.clock += 1;
self.entries.insert(
key,
GraphCacheEntry {
cached,
last_used: self.clock,
},
);
}
pub(crate) fn apply_stats(
&mut self,
key: &GraphCacheKey,
stat_map: &HashMap<git2::Oid, DiffStat>,
) {
if let Some(entry) = self.entries.get_mut(key) {
for row in &mut entry.cached.rows {
if let Some(stat) = stat_map.get(&row.oid) {
row.diff_stat = Some(stat.clone());
}
}
}
}
pub(crate) fn invalidate(&mut self, path: &Path) {
self.entries.retain(|key, _| key.path != path);
}
pub(crate) fn clear(&mut self) {
self.entries.clear();
}
fn evict_lru(&mut self) {
let Some((key, _)) = self
.entries
.iter()
.min_by_key(|(_, entry)| entry.last_used)
.map(|(key, entry)| (key.clone(), entry.last_used))
else {
return;
};
self.entries.remove(&key);
}
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub(crate) struct RowRenderKey {
pub(crate) oid: git2::Oid,
pub(crate) theme_generation: u64,
pub(crate) label_max_len: usize,
pub(crate) dimmed: bool,
pub(crate) collapsed: bool,
}