pitchfork_cli/
watch_files.rs1use crate::Result;
2use itertools::Itertools;
3use miette::IntoDiagnostic;
4use notify::{Config, EventKind, RecommendedWatcher, RecursiveMode};
5use notify_debouncer_full::{new_debouncer_opt, DebounceEventResult, Debouncer, FileIdMap};
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 tx.send(paths).await.unwrap();
40 }
41 }
42 });
43 },
44 FileIdMap::new(),
45 Config::default(),
46 )
47 .into_diagnostic()?;
48
49 Ok(Self { debouncer, rx })
50 }
51
52 pub fn watch(&mut self, path: &Path, recursive_mode: RecursiveMode) -> Result<()> {
53 self.debouncer.watch(path, recursive_mode).into_diagnostic()
54 }
55}