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