use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::sync::{Arc, OnceLock, RwLock};
use crate::project_root::{resolve_home, Marker};
use crate::search::index::Index;
type Registry = RwLock<HashMap<PathBuf, Arc<Index>>>;
static REGISTRY: OnceLock<Registry> = OnceLock::new();
fn registry() -> &'static Registry {
REGISTRY.get_or_init(|| RwLock::new(HashMap::new()))
}
fn key_for(root: &Path) -> PathBuf {
root.canonicalize().unwrap_or_else(|_| root.to_path_buf())
}
pub fn open_shared(path_arg: &Path, cwd: &Path) -> std::io::Result<Arc<Index>> {
let (home, _) = resolve_home(path_arg, cwd, Marker::SearchIndex);
let key = key_for(&home);
let cached = registry().read().unwrap().get(&key).cloned();
if let Some(cached) = cached {
if cached.is_fresh() {
return Ok(cached);
}
}
static LOAD_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
let _guard = LOAD_LOCK.lock().unwrap();
let cached = registry().read().unwrap().get(&key).cloned();
if let Some(cached) = cached {
if cached.is_fresh() {
return Ok(cached);
}
}
let fresh = Arc::new(Index::open(path_arg, cwd)?);
store(fresh.clone());
Ok(fresh)
}
pub fn store(index: Arc<Index>) {
let key = key_for(&index.paths.root);
registry().write().unwrap().insert(key, index);
}
#[cfg(test)]
pub fn forget(root: &Path) {
let key = key_for(root);
registry().write().unwrap().remove(&key);
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use std::io::Write;
#[test]
#[ignore]
fn open_shared_reuses_until_files_change() {
let dir = tempfile::tempdir().unwrap();
let root = dir.path();
fs::create_dir_all(root.join("src")).unwrap();
let write = |rel: &str, body: &str| {
let p = root.join(rel);
let mut f = fs::File::create(&p).unwrap();
f.write_all(body.as_bytes()).unwrap();
};
write("src/a.rs", "pub fn a() {}");
forget(root);
let first = open_shared(root, root).expect("build");
let second = open_shared(root, root).expect("reopen");
assert!(
Arc::ptr_eq(&first, &second),
"unchanged tree must reuse the cached Arc"
);
std::thread::sleep(std::time::Duration::from_millis(10));
write("src/b.rs", "pub fn b() {}");
let third = open_shared(root, root).expect("reopen after edit");
assert!(
!Arc::ptr_eq(&first, &third),
"edited tree must reload into a fresh Arc"
);
assert!(third.chunk_count() >= second.chunk_count());
forget(root);
}
}