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 let handles = self
36 .handles
37 .upgrade()
38 .context("unable to watch path, receiver dropped")?;
39 let mut handles = handles.lock();
40
41 // Return early if an ancestor of this path was already being watched.
42 if let Some((watched_path, _)) = handles
43 .range::<Path, _>((Bound::Unbounded, Bound::Included(path)))
44 .next_back()
45 && path.starts_with(watched_path)
46 {
47 return Ok(());
48 }
49
50 let (stream, handle) = EventStream::new(&[path], self.latency);
51 let tx = self.events_tx.clone();
52 thread::Builder::new()
53 .name("MacWatcher".to_owned())
54 .spawn(move || {
55 stream.run(move |events| smol::block_on(tx.send(events)).is_ok());
56 })
57 .unwrap();
58 handles.insert(path.into(), handle);
59
60 Ok(())
61 }
62
63 fn remove(&self, path: &Path) -> anyhow::Result<()> {
64 let handles = self
65 .handles
66 .upgrade()
67 .context("unable to remove path, receiver dropped")?;
68
69 let mut handles = handles.lock();
70 handles.remove(path);
71 Ok(())
72 }
73}