aboutsummaryrefslogtreecommitdiff
path: root/crates/server/src/vfs.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/server/src/vfs.rs')
-rw-r--r--crates/server/src/vfs.rs79
1 files changed, 79 insertions, 0 deletions
diff --git a/crates/server/src/vfs.rs b/crates/server/src/vfs.rs
new file mode 100644
index 000000000..a5c367494
--- /dev/null
+++ b/crates/server/src/vfs.rs
@@ -0,0 +1,79 @@
1use std::{
2 path::PathBuf,
3 thread,
4 fs,
5};
6
7use crossbeam_channel::{Sender, Receiver, bounded};
8use drop_bomb::DropBomb;
9use walkdir::WalkDir;
10
11use Result;
12
13
14pub struct FileEvent {
15 pub path: PathBuf,
16 pub kind: FileEventKind,
17}
18
19pub enum FileEventKind {
20 Add(String),
21 #[allow(unused)]
22 Remove,
23}
24
25pub struct Watcher {
26 thread: thread::JoinHandle<()>,
27 bomb: DropBomb,
28}
29
30impl Watcher {
31 pub fn stop(mut self) -> Result<()> {
32 self.bomb.defuse();
33 self.thread.join()
34 .map_err(|_| format_err!("file watcher died"))
35 }
36}
37
38pub fn watch(roots: Vec<PathBuf>) -> (Receiver<Vec<FileEvent>>, Watcher) {
39 let (sender, receiver) = bounded(16);
40 let thread = thread::spawn(move || run(roots, sender));
41 (receiver, Watcher {
42 thread,
43 bomb: DropBomb::new("Watcher should be stopped explicitly"),
44 })
45}
46
47fn run(roots: Vec<PathBuf>, sender: Sender<Vec<FileEvent>>) {
48 for root in roots {
49 let mut events = Vec::new();
50 for entry in WalkDir::new(root.as_path()) {
51 let entry = match entry {
52 Ok(entry) => entry,
53 Err(e) => {
54 warn!("watcher error: {}", e);
55 continue;
56 }
57 };
58 if !entry.file_type().is_file() {
59 continue;
60 }
61 let path = entry.path();
62 if path.extension().and_then(|os| os.to_str()) != Some("rs") {
63 continue;
64 }
65 let text = match fs::read_to_string(path) {
66 Ok(text) => text,
67 Err(e) => {
68 warn!("watcher error: {}", e);
69 continue;
70 }
71 };
72 events.push(FileEvent {
73 path: path.to_owned(),
74 kind: FileEventKind::Add(text),
75 })
76 }
77 sender.send(events)
78 }
79}