pitchfork_cli/
watch_files.rs1use 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 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}