1use std::path::Path;
2use std::sync::mpsc;
3use std::time::{Duration, Instant};
4
5use anyhow::Result;
6use notify::{Config, RecursiveMode, Watcher};
7
8use crate::{is_document_file, DocIndex};
9
10pub fn watch_docs(
11 root: &Path,
12 debounce_ms: u64,
13 stop_rx: mpsc::Receiver<()>,
14 log_prefix: &str,
15) -> Result<()> {
16 let (tx, rx) = mpsc::channel();
17 let config = Config::default().with_poll_interval(Duration::from_millis(debounce_ms));
18 let mut watcher = notify::RecommendedWatcher::new(tx, config)?;
19 watcher.watch(root, RecursiveMode::Recursive)?;
20
21 let debounce = Duration::from_millis(debounce_ms);
22 let mut last_reindex = Instant::now();
23 let mut pending = false;
24
25 loop {
26 if stop_rx.try_recv().is_ok() {
27 eprintln!("[{log_prefix}] stopped");
28 break;
29 }
30
31 match rx.recv_timeout(Duration::from_millis(500)) {
32 Ok(Ok(event)) => {
33 if event.paths.iter().any(|p| is_document_file(p)) {
34 pending = true;
35 }
36 }
37 Ok(Err(e)) => eprintln!("[{log_prefix}] watch error: {e}"),
38 Err(mpsc::RecvTimeoutError::Timeout) => {}
39 Err(mpsc::RecvTimeoutError::Disconnected) => break,
40 }
41
42 if pending && last_reindex.elapsed() >= debounce {
43 eprintln!("[{log_prefix}] document change detected, reindexing...");
44 let mut idx = match DocIndex::open(root) {
45 Ok(i) => i,
46 Err(e) => {
47 eprintln!("[{log_prefix}] open error: {e}");
48 pending = false;
49 continue;
50 }
51 };
52 if let Err(e) = idx.init() {
53 eprintln!("[{log_prefix}] init error: {e}");
54 } else {
55 match idx.index() {
56 Ok(r) => eprintln!(
57 "[{log_prefix}] reindexed: {} files, {} chunks",
58 r.indexed_files, r.total_chunks
59 ),
60 Err(e) => eprintln!("[{log_prefix}] index error: {e}"),
61 }
62 }
63 pending = false;
64 last_reindex = Instant::now();
65 }
66 }
67
68 Ok(())
69}