aboutsummaryrefslogtreecommitdiff
path: root/crates/server/src/vfs.rs
blob: a5c3674940b4327577ec5712de2892d273ce0fb6 (plain)
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
use std::{
    path::PathBuf,
    thread,
    fs,
};

use crossbeam_channel::{Sender, Receiver, bounded};
use drop_bomb::DropBomb;
use walkdir::WalkDir;

use Result;


pub struct FileEvent {
    pub path: PathBuf,
    pub kind: FileEventKind,
}

pub enum FileEventKind {
    Add(String),
    #[allow(unused)]
    Remove,
}

pub struct Watcher {
    thread: thread::JoinHandle<()>,
    bomb: DropBomb,
}

impl Watcher {
    pub fn stop(mut self) -> Result<()> {
        self.bomb.defuse();
        self.thread.join()
            .map_err(|_| format_err!("file watcher died"))
    }
}

pub fn watch(roots: Vec<PathBuf>) -> (Receiver<Vec<FileEvent>>, Watcher) {
    let (sender, receiver) = bounded(16);
    let thread = thread::spawn(move || run(roots, sender));
    (receiver, Watcher {
        thread,
        bomb: DropBomb::new("Watcher should be stopped explicitly"),
    })
}

fn run(roots: Vec<PathBuf>, sender: Sender<Vec<FileEvent>>) {
    for root in roots {
        let mut events = Vec::new();
        for entry in WalkDir::new(root.as_path()) {
            let entry = match entry {
                Ok(entry) => entry,
                Err(e) => {
                    warn!("watcher error: {}", e);
                    continue;
                }
            };
            if !entry.file_type().is_file() {
                continue;
            }
            let path = entry.path();
            if path.extension().and_then(|os| os.to_str()) != Some("rs") {
                continue;
            }
            let text = match fs::read_to_string(path) {
                Ok(text) => text,
                Err(e) => {
                    warn!("watcher error: {}", e);
                    continue;
                }
            };
            events.push(FileEvent {
                path: path.to_owned(),
                kind: FileEventKind::Add(text),
            })
        }
        sender.send(events)
    }
}