pitchfork_cli/
watch_files.rs

1use crate::Result;
2use itertools::Itertools;
3use miette::IntoDiagnostic;
4use notify::{Config, EventKind, RecommendedWatcher, RecursiveMode};
5use notify_debouncer_full::{DebounceEventResult, Debouncer, FileIdMap, new_debouncer_opt};
6use std::path::{Path, PathBuf};
7use std::time::Duration;
8
9pub struct WatchFiles {
10    pub rx: tokio::sync::mpsc::Receiver<Vec<PathBuf>>,
11    debouncer: Debouncer<RecommendedWatcher, FileIdMap>,
12}
13
14impl WatchFiles {
15    pub fn new(duration: Duration) -> Result<Self> {
16        let h = tokio::runtime::Handle::current();
17        let (tx, rx) = tokio::sync::mpsc::channel(1);
18        let debouncer = new_debouncer_opt(
19            duration,
20            None,
21            move |res: DebounceEventResult| {
22                let tx = tx.clone();
23                h.spawn(async move {
24                    if let Ok(ev) = res {
25                        let paths = ev
26                            .into_iter()
27                            .filter(|e| {
28                                matches!(
29                                    e.kind,
30                                    EventKind::Modify(_)
31                                        | EventKind::Create(_)
32                                        | EventKind::Remove(_)
33                                )
34                            })
35                            .flat_map(|e| e.paths.clone())
36                            .unique()
37                            .collect_vec();
38                        if !paths.is_empty() {
39                            // Ignore send errors - receiver may be dropped during shutdown
40                            let _ = tx.send(paths).await;
41                        }
42                    }
43                });
44            },
45            FileIdMap::new(),
46            Config::default(),
47        )
48        .into_diagnostic()?;
49
50        Ok(Self { debouncer, rx })
51    }
52
53    pub fn watch(&mut self, path: &Path, recursive_mode: RecursiveMode) -> Result<()> {
54        self.debouncer.watch(path, recursive_mode).into_diagnostic()
55    }
56}