use indexmap::IndexMap;
use std::path::Path;
use std::sync::{Arc, RwLock};
const MAX_CACHE_ENTRIES: usize = 100;
#[derive(Debug, Clone)]
pub struct FileStateEntry {
pub content: String,
pub timestamp: u64,
pub offset: Option<usize>,
pub limit: Option<usize>,
pub is_partial_view: bool,
}
impl FileStateEntry {
pub fn is_partial_view(&self) -> bool {
self.is_partial_view
}
}
async fn normalize_cache_key(path: &str) -> String {
match tokio::fs::canonicalize(Path::new(path)).await {
Ok(canonical) => canonical.to_string_lossy().to_string(),
Err(_) => path.to_string(),
}
}
#[derive(Debug, Clone)]
pub struct FileStateCache {
inner: Arc<RwLock<IndexMap<String, FileStateEntry>>>,
}
impl FileStateCache {
pub fn new() -> Self {
Self {
inner: Arc::new(RwLock::new(IndexMap::new())),
}
}
pub async fn get(&self, path: &str) -> Option<FileStateEntry> {
let key = normalize_cache_key(path).await;
self.inner.read().unwrap().get(&key).cloned()
}
pub async fn set(&self, path: String, entry: FileStateEntry) {
let key = normalize_cache_key(&path).await;
let mut inner = self.inner.write().unwrap();
if inner.len() >= MAX_CACHE_ENTRIES && !inner.contains_key(&key) {
if let Some(first_key) = inner.keys().next().cloned() {
inner.shift_remove(&first_key);
}
}
inner.insert(key, entry);
}
pub fn clear(&self) {
self.inner.write().unwrap().clear();
}
}