use std::path::Path;
use std::sync::atomic::{AtomicBool, Ordering};
use anyhow::Result;
use notify::{Config, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
use crate::graph::Store;
use crate::pipeline::{run_pipeline, PipelineConfig};
const DEBOUNCE_SECS: u64 = 2;
pub fn watch_and_reindex(store: &Store, root: &Path, config: &PipelineConfig) -> Result<()> {
run_pipeline(store, root, config)?;
println!(
"Watching {} (re-index in {}s after changes). Ctrl+C to stop.",
root.display(),
DEBOUNCE_SECS
);
let running = std::sync::Arc::new(AtomicBool::new(true));
let running_clone = std::sync::Arc::clone(&running);
ctrlc::set_handler(move || {
running_clone.store(false, Ordering::Relaxed);
})?;
let dirty = std::sync::Arc::new(AtomicBool::new(false));
let dirty_clone = std::sync::Arc::clone(&dirty);
let root_path = root.to_path_buf();
let mut watcher = RecommendedWatcher::new(
move |res: Result<notify::Event, notify::Error>| {
if let Ok(ev) = res {
if matches!(
ev.kind,
EventKind::Modify(_) | EventKind::Create(_) | EventKind::Remove(_)
) {
let has_rs = ev.paths.iter().any(|p| {
!p.as_os_str().to_string_lossy().contains("target")
&& p.extension().is_some_and(|e| e == "rs")
});
if has_rs {
dirty_clone.store(true, Ordering::Relaxed);
}
}
}
},
Config::default(),
)?;
watcher.watch(root, RecursiveMode::Recursive)?;
while running.load(Ordering::Relaxed) {
std::thread::sleep(std::time::Duration::from_secs(DEBOUNCE_SECS));
if !running.load(Ordering::Relaxed) {
break;
}
if dirty.swap(false, Ordering::SeqCst) {
match Store::new_memory().and_then(|temp_store| {
run_pipeline(&temp_store, &root_path, config)?;
store.clear()?;
store.copy_from(&temp_store)?;
Ok(())
}) {
Ok(()) => println!("Re-indexed {}", root_path.display()),
Err(e) => eprintln!("Re-index failed: {e:#}"),
}
}
}
println!("Stopped watching.");
Ok(())
}