mod detector;
pub use detector::{
create_detector, ChangeDetectionStrategy, ChangeDetector, ChangeStatus, FileChangeInfo,
FileSignatureDetector, GitStatusDetector,
};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::time::SystemTime;
use crate::errors::{SearchError, SearchResult};
use crate::results::Match;
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct IncrementalCache {
pub files: HashMap<PathBuf, FileCacheEntry>,
pub metadata: CacheMetadata,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct FileCacheEntry {
pub signature: FileSignature,
pub search_results: Option<Vec<Match>>,
pub last_accessed: SystemTime,
pub access_count: u64,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct FileSignature {
pub mtime: SystemTime,
pub size: u64,
pub hash: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct CacheMetadata {
pub version: String,
pub last_search_timestamp: SystemTime,
pub hit_rate: f64,
pub compression_ratio: Option<f64>,
pub frequently_changed: Vec<PathBuf>,
}
impl Default for CacheMetadata {
fn default() -> Self {
Self {
version: env!("CARGO_PKG_VERSION").to_string(),
last_search_timestamp: SystemTime::now(),
hit_rate: 0.0,
compression_ratio: None,
frequently_changed: Vec::new(),
}
}
}
impl IncrementalCache {
pub fn new() -> Self {
Self {
files: HashMap::new(),
metadata: CacheMetadata {
version: env!("CARGO_PKG_VERSION").to_string(),
last_search_timestamp: SystemTime::now(),
hit_rate: 0.0,
compression_ratio: None,
frequently_changed: Vec::new(),
},
}
}
pub fn load_from(path: &Path) -> SearchResult<Self> {
if !path.exists() {
return Ok(Self::new());
}
let data = match std::fs::read(path) {
Ok(data) => data,
Err(_) => return Ok(Self::new()),
};
match serde_json::from_slice(&data) {
Ok(cache) => Ok(cache),
Err(_) => {
Ok(Self::new())
}
}
}
pub fn save_to(&self, path: &Path) -> SearchResult<()> {
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent).map_err(SearchError::IoError)?;
}
let tmp_path = path.with_extension("tmp");
let data =
serde_json::to_vec_pretty(self).map_err(|e| SearchError::CacheError(e.to_string()))?;
std::fs::write(&tmp_path, data).map_err(SearchError::IoError)?;
std::fs::rename(&tmp_path, path).map_err(SearchError::IoError)?;
Ok(())
}
pub fn update_stats(&mut self, hits: usize, total: usize) {
if total > 0 {
self.metadata.hit_rate = hits as f64 / total as f64;
}
self.metadata.last_search_timestamp = SystemTime::now();
}
}
impl FileCacheEntry {
pub fn new(signature: FileSignature) -> Self {
Self {
signature,
search_results: None,
last_accessed: SystemTime::now(),
access_count: 0,
}
}
pub fn mark_accessed(&mut self) {
self.last_accessed = SystemTime::now();
self.access_count += 1;
}
}