aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_vfs/src/watcher.rs
diff options
context:
space:
mode:
authorBernardo <[email protected]>2019-01-06 14:05:12 +0000
committerAleksey Kladov <[email protected]>2019-01-26 08:46:16 +0000
commit1d5eaefe8a8e4f8b267d51ee8ece866741586ada (patch)
tree04c740f694ecacdd84a635256b9a3d483f45f593 /crates/ra_vfs/src/watcher.rs
parentac757e114e9dcbc70600803dd4adc4f99ecde78e (diff)
initial Watcher impl
Diffstat (limited to 'crates/ra_vfs/src/watcher.rs')
-rw-r--r--crates/ra_vfs/src/watcher.rs96
1 files changed, 96 insertions, 0 deletions
diff --git a/crates/ra_vfs/src/watcher.rs b/crates/ra_vfs/src/watcher.rs
new file mode 100644
index 000000000..cc05f949e
--- /dev/null
+++ b/crates/ra_vfs/src/watcher.rs
@@ -0,0 +1,96 @@
1use std::{
2 path::{Path, PathBuf},
3 sync::mpsc,
4 thread,
5 time::Duration,
6};
7
8use crossbeam_channel::Receiver;
9use drop_bomb::DropBomb;
10use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher as NotifyWatcher};
11
12pub struct Watcher {
13 receiver: Receiver<WatcherChange>,
14 watcher: RecommendedWatcher,
15 thread: thread::JoinHandle<()>,
16 bomb: DropBomb,
17}
18
19#[derive(Debug)]
20pub enum WatcherChange {
21 Create(PathBuf),
22 Write(PathBuf),
23 Remove(PathBuf),
24 Rename(PathBuf, PathBuf),
25}
26
27impl WatcherChange {
28 fn from_debounced_event(ev: DebouncedEvent) -> Option<WatcherChange> {
29 match ev {
30 DebouncedEvent::NoticeWrite(_)
31 | DebouncedEvent::NoticeRemove(_)
32 | DebouncedEvent::Chmod(_)
33 | DebouncedEvent::Rescan => {
34 // ignore
35 None
36 }
37 DebouncedEvent::Create(path) => Some(WatcherChange::Create(path)),
38 DebouncedEvent::Write(path) => Some(WatcherChange::Write(path)),
39 DebouncedEvent::Remove(path) => Some(WatcherChange::Remove(path)),
40 DebouncedEvent::Rename(src, dst) => Some(WatcherChange::Rename(src, dst)),
41 DebouncedEvent::Error(err, path) => {
42 // TODO
43 log::warn!("watch error {}, {:?}", err, path);
44 None
45 }
46 }
47 }
48}
49
50impl Watcher {
51 pub fn new() -> Result<Watcher, Box<std::error::Error>> {
52 let (input_sender, input_receiver) = mpsc::channel();
53 let watcher = notify::watcher(input_sender, Duration::from_millis(250))?;
54 let (output_sender, output_receiver) = crossbeam_channel::unbounded();
55 let thread = thread::spawn(move || loop {
56 match input_receiver.recv() {
57 Ok(ev) => {
58 // forward relevant events only
59 if let Some(change) = WatcherChange::from_debounced_event(ev) {
60 output_sender.send(change).unwrap();
61 }
62 }
63 Err(err) => {
64 log::debug!("Watcher stopped ({})", err);
65 break;
66 }
67 }
68 });
69 Ok(Watcher {
70 receiver: output_receiver,
71 watcher,
72 thread,
73 bomb: DropBomb::new(format!("Watcher was not shutdown")),
74 })
75 }
76
77 pub fn watch(&mut self, root: impl AsRef<Path>) -> Result<(), Box<std::error::Error>> {
78 self.watcher.watch(root, RecursiveMode::Recursive)?;
79 Ok(())
80 }
81
82 pub fn change_receiver(&self) -> &Receiver<WatcherChange> {
83 &self.receiver
84 }
85
86 pub fn shutdown(mut self) -> thread::Result<()> {
87 self.bomb.defuse();
88 drop(self.watcher);
89 let res = self.thread.join();
90 match &res {
91 Ok(()) => log::info!("... Watcher terminated with ok"),
92 Err(_) => log::error!("... Watcher terminated with err"),
93 }
94 res
95 }
96}