use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Arc, Mutex};
use std::time::{Instant, SystemTime};
use crate::graph::CodeGraph;
pub struct CachedIndex {
pub graph: Arc<CodeGraph>,
pub loaded_at: Instant,
last_accessed: AtomicInstant,
pub file_mtime: SystemTime,
pub query_count: AtomicU64,
}
impl CachedIndex {
#[must_use]
pub fn new(graph: Arc<CodeGraph>, file_mtime: SystemTime) -> Self {
let now = Instant::now();
Self {
graph,
loaded_at: now,
last_accessed: AtomicInstant::new(now),
file_mtime,
query_count: AtomicU64::new(0),
}
}
pub fn access(&self) {
self.last_accessed.store(Instant::now());
self.query_count.fetch_add(1, Ordering::Relaxed);
}
pub fn last_accessed(&self) -> Instant {
self.last_accessed.load()
}
#[cfg(test)]
pub fn set_last_accessed(&self, instant: Instant) {
self.last_accessed.store(instant);
}
pub fn query_count(&self) -> u64 {
self.query_count.load(Ordering::Relaxed)
}
}
pub struct AtomicInstant {
inner: Mutex<Instant>,
}
impl AtomicInstant {
fn new(instant: Instant) -> Self {
Self {
inner: Mutex::new(instant),
}
}
fn store(&self, instant: Instant) {
*self
.inner
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) = instant;
}
fn load(&self) -> Instant {
*self
.inner
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;
#[test]
fn access_updates_tracking() {
let graph = Arc::new(CodeGraph::new());
let cached = CachedIndex::new(graph, SystemTime::now());
assert_eq!(cached.query_count(), 0);
let before = cached.last_accessed();
cached.access();
assert_eq!(cached.query_count(), 1);
assert!(cached.last_accessed() >= before);
}
#[test]
fn set_last_accessed_allows_manual_adjustment() {
let graph = Arc::new(CodeGraph::new());
let cached = CachedIndex::new(graph, SystemTime::now());
let past = Instant::now().checked_sub(Duration::from_secs(5)).unwrap();
cached.set_last_accessed(past);
assert!(cached.last_accessed() <= Instant::now());
}
}