-
-
Notifications
You must be signed in to change notification settings - Fork 119
/
watcher.rs
90 lines (77 loc) · 3.17 KB
/
watcher.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
use std::{collections::HashSet, ffi::OsStr, path::Path, thread};
use notify::Watcher as _;
use crate::{evaluator::TriggerEvaluation, Error, Evaluator};
/// Watches a model for changes, reloading it continually
pub struct Watcher {
_watcher: Box<dyn notify::Watcher>,
}
impl Watcher {
/// Watch the provided model for changes
pub fn watch_model(
watch_path: impl AsRef<Path>,
evaluator: &Evaluator,
) -> Result<Self, Error> {
let watch_path = watch_path.as_ref();
let watch_tx = evaluator.trigger();
let watch_tx_2 = evaluator.trigger();
let mut watcher = notify::recommended_watcher(
move |event: notify::Result<notify::Event>| {
// Unfortunately the `notify` documentation doesn't say when
// this might happen, so no idea if it needs to be handled.
let event = event.expect("Error handling watch event");
// Various acceptable ModifyKind kinds. Varies across platforms
// (e.g. MacOs vs. Windows10)
if let notify::EventKind::Modify(
notify::event::ModifyKind::Any
| notify::event::ModifyKind::Data(
notify::event::DataChange::Any
| notify::event::DataChange::Content,
),
) = event.kind
{
let file_ext = event
.paths
.get(0)
.expect("File path missing in watch event")
.extension();
let black_list = HashSet::from([
OsStr::new("swp"),
OsStr::new("tmp"),
OsStr::new("swx"),
]);
if let Some(ext) = file_ext {
if black_list.contains(ext) {
return;
}
}
// This will panic, if the other end is disconnected, which
// is probably the result of a panic on that thread, or the
// application is being shut down.
//
// Either way, not much we can do about it here.
watch_tx
.send(TriggerEvaluation)
.expect("Channel is disconnected");
}
},
)?;
watcher.watch(watch_path, notify::RecursiveMode::Recursive)?;
// To prevent a race condition between the initial load and the start of
// watching, we'll trigger the initial load here, after having started
// watching.
//
// This happens in a separate thread, because the channel is bounded and
// has no buffer.
//
// Will panic, if the receiving end has panicked. Not much we can do
// about that, if it happened.
thread::spawn(move || {
watch_tx_2
.send(TriggerEvaluation)
.expect("Channel is disconnected");
});
Ok(Self {
_watcher: Box::new(watcher),
})
}
}