1use crate::Watcher;
2use anyhow::{Context as _, Result};
3use collections::{BTreeMap, Bound};
4use fsevent::EventStream;
5use parking_lot::Mutex;
6use std::{
7 path::{Path, PathBuf},
8 sync::Weak,
9 time::Duration,
10};
11
12pub struct MacWatcher {
13 events_tx: smol::channel::Sender<Vec<fsevent::Event>>,
14 handles: Weak<Mutex<BTreeMap<PathBuf, fsevent::Handle>>>,
15 latency: Duration,
16}
17
18impl MacWatcher {
19 pub fn new(
20 events_tx: smol::channel::Sender<Vec<fsevent::Event>>,
21 handles: Weak<Mutex<BTreeMap<PathBuf, fsevent::Handle>>>,
22 latency: Duration,
23 ) -> Self {
24 Self {
25 events_tx,
26 handles,
27 latency,
28 }
29 }
30}
31
32impl Watcher for MacWatcher {
33 fn add(&self, path: &Path) -> Result<()> {
34 let handles = self
35 .handles
36 .upgrade()
37 .context("unable to watch path, receiver dropped")?;
38 let mut handles = handles.lock();
39
40 // Return early if an ancestor of this path was already being watched.
41 if let Some((watched_path, _)) = handles
42 .range::<Path, _>((Bound::Unbounded, Bound::Included(path)))
43 .next_back()
44 {
45 if path.starts_with(watched_path) {
46 return Ok(());
47 }
48 }
49
50 let (stream, handle) = EventStream::new(&[path], self.latency);
51 let tx = self.events_tx.clone();
52 std::thread::spawn(move || {
53 stream.run(move |events| smol::block_on(tx.send(events)).is_ok());
54 });
55 handles.insert(path.into(), handle);
56
57 Ok(())
58 }
59
60 fn remove(&self, path: &Path) -> anyhow::Result<()> {
61 let handles = self
62 .handles
63 .upgrade()
64 .context("unable to remove path, receiver dropped")?;
65
66 let mut handles = handles.lock();
67 handles.remove(path);
68 Ok(())
69 }
70}