Skip to main content

chaud_hot/workspace/
watcher.rs

1use super::graph::{Graph, KrateDir};
2use crate::util::latest::{LatestPublisher, LatestReader, make_latest};
3use anyhow::{Context as _, Result, ensure};
4use core::ops;
5use hashbrown::HashMap;
6use notify::{
7    EventHandler, EventKind, RecommendedWatcher, RecursiveMode, Watcher as _, recommended_watcher,
8};
9use std::time::Instant;
10
11pub struct Watcher {
12    #[expect(dead_code, reason = "keep alive")]
13    inner: RecommendedWatcher,
14    latest: LatestReader<Instant>,
15}
16
17impl ops::Deref for Watcher {
18    type Target = LatestReader<Instant>;
19
20    fn deref(&self) -> &Self::Target {
21        &self.latest
22    }
23}
24
25impl ops::DerefMut for Watcher {
26    fn deref_mut(&mut self) -> &mut Self::Target {
27        &mut self.latest
28    }
29}
30
31impl Watcher {
32    pub fn new(graph: &'static Graph) -> Result<Self> {
33        new_inner(graph).context("Failed to create watcher")
34    }
35}
36
37fn new_inner(graph: &'static Graph) -> Result<Watcher> {
38    let (publisher, reader) = make_latest(Instant::now());
39
40    let dirs = extract_dirs(graph)?;
41
42    let mut inner =
43        recommended_watcher(EvHandler { latest: publisher, reported_event_err: false })?;
44
45    for dir in &dirs {
46        inner.watch(dir.path().as_std_path(), dir.rec_mode())?;
47        log::trace!("Watching: {:?} ({:?})", dir.path(), dir.rec_mode());
48    }
49
50    log::debug!("Watching {} paths", dirs.len());
51
52    Ok(Watcher { inner, latest: reader })
53}
54
55impl KrateDir {
56    fn rec_mode(&self) -> RecursiveMode {
57        match self {
58            KrateDir::Src(_) => RecursiveMode::Recursive,
59            KrateDir::Root(_) => RecursiveMode::NonRecursive,
60        }
61    }
62}
63
64struct EvHandler {
65    latest: LatestPublisher<Instant>,
66    reported_event_err: bool,
67}
68
69impl EventHandler for EvHandler {
70    fn handle_event(&mut self, event: notify::Result<notify::Event>) {
71        let event = match event {
72            Ok(ev) => ev,
73            Err(err) => return self.report_event_err(&err),
74        };
75
76        match event.kind {
77            EventKind::Any | EventKind::Other | EventKind::Access(_) => return,
78            EventKind::Create(_) | EventKind::Modify(_) | EventKind::Remove(_) => (),
79        }
80
81        if !event.paths.is_empty() {
82            self.latest.publish(Instant::now());
83        }
84    }
85}
86
87impl EvHandler {
88    fn report_event_err(&mut self, err: &notify::Error) {
89        if self.reported_event_err {
90            log::trace!("Watcher error: {err}");
91        } else {
92            self.reported_event_err = true;
93            log::warn!("Watcher error: {err}");
94            log::warn!("Future Watcher errors will be logged only as TRACE");
95        }
96    }
97}
98
99fn extract_dirs(graph: &Graph) -> Result<Box<[&KrateDir]>> {
100    let mut dirs = HashMap::new();
101
102    for krate in graph.collect_krates_to_watch() {
103        for dir in krate.dirs() {
104            let path = dir.path();
105
106            let did_insert = dirs.try_insert(path, dir).is_ok();
107
108            ensure!(did_insert, "Duplicate crate path: {path:?}");
109        }
110    }
111
112    let mut dirs: Box<[_]> = dirs.into_values().collect();
113    dirs.sort_unstable_by_key(|dir| dir.path());
114
115    Ok(dirs)
116}