aboutsummaryrefslogtreecommitdiff
path: root/crates/vfs/src/walkdir_loader.rs
blob: 13e59e3f3435252001a7386f26da8f40ba332110 (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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
//! A walkdir-based implementation of `loader::Handle`, which doesn't try to
//! watch files.
use std::convert::TryFrom;

use globset::{Glob, GlobSetBuilder};
use paths::{AbsPath, AbsPathBuf};
use walkdir::WalkDir;

use crate::loader;

#[derive(Debug)]
pub struct WalkdirLoaderHandle {
    // Relative order of fields below is significant.
    sender: crossbeam_channel::Sender<Message>,
    _thread: jod_thread::JoinHandle,
}

enum Message {
    Config(loader::Config),
    Invalidate(AbsPathBuf),
}

impl loader::Handle for WalkdirLoaderHandle {
    fn spawn(sender: loader::Sender) -> WalkdirLoaderHandle {
        let actor = WalkdirLoaderActor { sender };
        let (sender, receiver) = crossbeam_channel::unbounded::<Message>();
        let thread = jod_thread::spawn(move || actor.run(receiver));
        WalkdirLoaderHandle { sender, _thread: thread }
    }
    fn set_config(&mut self, config: loader::Config) {
        self.sender.send(Message::Config(config)).unwrap()
    }
    fn invalidate(&mut self, path: AbsPathBuf) {
        self.sender.send(Message::Invalidate(path)).unwrap();
    }
    fn load_sync(&mut self, path: &AbsPathBuf) -> Option<Vec<u8>> {
        read(path)
    }
}

struct WalkdirLoaderActor {
    sender: loader::Sender,
}

impl WalkdirLoaderActor {
    fn run(mut self, receiver: crossbeam_channel::Receiver<Message>) {
        for msg in receiver {
            match msg {
                Message::Config(config) => {
                    self.send(loader::Message::DidSwitchConfig { n_entries: config.load.len() });
                    for entry in config.load.into_iter() {
                        let files = self.load_entry(entry);
                        self.send(loader::Message::Loaded { files });
                    }
                    drop(config.watch);
                    self.send(loader::Message::DidLoadAllEntries);
                }
                Message::Invalidate(path) => {
                    let contents = read(path.as_path());
                    let files = vec![(path, contents)];
                    self.send(loader::Message::Loaded { files });
                }
            }
        }
    }
    fn load_entry(&mut self, entry: loader::Entry) -> Vec<(AbsPathBuf, Option<Vec<u8>>)> {
        match entry {
            loader::Entry::Files(files) => files
                .into_iter()
                .map(|file| {
                    let contents = read(file.as_path());
                    (file, contents)
                })
                .collect::<Vec<_>>(),
            loader::Entry::Directory { path, globs } => {
                let globset = {
                    let mut builder = GlobSetBuilder::new();
                    for glob in &globs {
                        builder.add(Glob::new(glob).unwrap());
                    }
                    builder.build().unwrap()
                };

                let files = WalkDir::new(path)
                    .into_iter()
                    .filter_map(|it| it.ok())
                    .filter(|it| it.file_type().is_file())
                    .map(|it| it.into_path())
                    .map(|it| AbsPathBuf::try_from(it).unwrap())
                    .filter(|it| globset.is_match(&it));

                files
                    .map(|file| {
                        let contents = read(file.as_path());
                        (file, contents)
                    })
                    .collect()
            }
        }
    }
    fn send(&mut self, msg: loader::Message) {
        (self.sender)(msg)
    }
}

fn read(path: &AbsPath) -> Option<Vec<u8>> {
    std::fs::read(path).ok()
}