use super::pmat_query_types::{PmatQueryOptions, PmatQueryResult};
pub(super) fn cache_dir() -> std::path::PathBuf {
dirs::cache_dir()
.unwrap_or_else(|| std::path::PathBuf::from("/tmp"))
.join("batuta")
.join("pmat-query")
}
pub(super) fn cache_key(query: &str, project_path: Option<&str>) -> String {
let input = format!("{}:{}", query, project_path.unwrap_or("."));
let mut hash: u64 = 0xcbf2_9ce4_8422_2325;
for byte in input.as_bytes() {
hash ^= *byte as u64;
hash = hash.wrapping_mul(0x0100_0000_01b3);
}
format!("{:016x}", hash)
}
fn any_source_newer_than(
project_path: &std::path::Path,
cache_mtime: std::time::SystemTime,
) -> bool {
let walker = match glob::glob(&format!("{}/**/*.rs", project_path.display())) {
Ok(w) => w,
Err(_) => return true,
};
for entry in walker.flatten() {
if let Ok(meta) = entry.metadata() {
if let Ok(mtime) = meta.modified() {
if mtime > cache_mtime {
return true;
}
}
}
}
false
}
pub(super) fn load_cached_results(
query: &str,
project_path: Option<&str>,
) -> Option<Vec<PmatQueryResult>> {
let key = cache_key(query, project_path);
let path = cache_dir().join(format!("{key}.json"));
let cache_mtime = path.metadata().ok()?.modified().ok()?;
let proj = std::path::Path::new(project_path.unwrap_or("."));
if any_source_newer_than(proj, cache_mtime) {
return None;
}
let data = std::fs::read_to_string(&path).ok()?;
serde_json::from_str(&data).ok()
}
pub(super) fn save_cache(query: &str, project_path: Option<&str>, results: &[PmatQueryResult]) {
let dir = cache_dir();
let _ = std::fs::create_dir_all(&dir);
let key = cache_key(query, project_path);
let path = dir.join(format!("{key}.json"));
if let Ok(json) = serde_json::to_string(results) {
let _ = std::fs::write(path, json);
}
}
pub(super) fn attach_rag_backlinks(
pmat_results: &mut [PmatQueryResult],
chunk_contents: &std::collections::HashMap<String, String>,
) {
for result in pmat_results.iter_mut() {
let file_suffix = &result.file_path;
for key in chunk_contents.keys() {
if key.contains(file_suffix) {
result.rag_backlinks.push(key.clone());
}
}
result.rag_backlinks.sort();
result.rag_backlinks.dedup();
result.rag_backlinks.truncate(3);
}
}
pub(super) fn run_cross_project_query(
opts: &PmatQueryOptions,
) -> anyhow::Result<Vec<PmatQueryResult>> {
use crate::ansi_colors::Colorize;
use crate::oracle::LocalWorkspaceOracle;
let mut oracle_ws = LocalWorkspaceOracle::new()?;
oracle_ws.discover_projects()?;
let projects: Vec<_> = oracle_ws.projects().iter().collect();
eprintln!(" {} Searching {} projects in parallel...", "[pmat-all]".dimmed(), projects.len());
let all_chunk_results: Vec<Vec<PmatQueryResult>> = std::thread::scope(|s| {
let handles: Vec<_> = projects
.iter()
.map(|(name, project)| {
let project_opts = PmatQueryOptions {
query: opts.query.clone(),
project_path: Some(project.path.to_string_lossy().to_string()),
limit: opts.limit,
min_grade: opts.min_grade.clone(),
max_complexity: opts.max_complexity,
include_source: opts.include_source,
};
let name = (*name).clone();
s.spawn(move || match super::pmat_query_display::run_pmat_query(&project_opts) {
Ok(mut results) => {
for r in &mut results {
r.project = Some(name.clone());
r.file_path = format!("{}/{}", name, r.file_path);
}
results
}
Err(_) => Vec::new(),
})
})
.collect();
handles.into_iter().filter_map(|h| h.join().ok()).collect()
});
let mut all_results: Vec<PmatQueryResult> = all_chunk_results.into_iter().flatten().collect();
all_results.sort_by(|a, b| {
b.relevance_score.partial_cmp(&a.relevance_score).unwrap_or(std::cmp::Ordering::Equal)
});
all_results.truncate(opts.limit);
Ok(all_results)
}