chaud_hot/workspace/
watcher.rs1use 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: ¬ify::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}