mac_watcher.rs

 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}