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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
use std::{fs, sync::Arc, thread};
use crossbeam_channel::{Receiver, Sender};
use relative_path::RelativePathBuf;
use thread_worker::WorkerHandle;
use walkdir::WalkDir;
mod watcher;
use watcher::Watcher;
use crate::{RootFilter, Roots, VfsRoot};
pub(crate) enum Task {
AddRoot {
root: VfsRoot,
filter: Arc<RootFilter>,
},
}
#[derive(Debug)]
pub enum TaskResult {
BulkLoadRoot {
root: VfsRoot,
files: Vec<(RelativePathBuf, String)>,
},
AddSingleFile {
root: VfsRoot,
path: RelativePathBuf,
text: String,
},
ChangeSingleFile {
root: VfsRoot,
path: RelativePathBuf,
text: String,
},
RemoveSingleFile {
root: VfsRoot,
path: RelativePathBuf,
},
}
pub(crate) struct Worker {
worker: thread_worker::Worker<Task, TaskResult>,
worker_handle: WorkerHandle,
}
impl Worker {
pub(crate) fn start(roots: Arc<Roots>) -> Worker {
let (worker, worker_handle) =
thread_worker::spawn("vfs", 128, move |input_receiver, output_sender| {
let mut watcher = match Watcher::start(roots, output_sender.clone()) {
Ok(w) => Some(w),
Err(e) => {
log::error!("could not start watcher: {}", e);
None
}
};
let res = input_receiver
.into_iter()
.filter_map(|t| handle_task(t, &mut watcher))
.try_for_each(|it| output_sender.send(it));
if let Some(watcher) = watcher {
let _ = watcher.shutdown();
}
res.unwrap()
});
Worker {
worker,
worker_handle,
}
}
pub(crate) fn sender(&self) -> &Sender<Task> {
&self.worker.inp
}
pub(crate) fn receiver(&self) -> &Receiver<TaskResult> {
&self.worker.out
}
pub(crate) fn shutdown(self) -> thread::Result<()> {
let _ = self.worker.shutdown();
self.worker_handle.shutdown()
}
}
fn handle_task(task: Task, watcher: &mut Option<Watcher>) -> Option<TaskResult> {
match task {
Task::AddRoot { root, filter } => {
if let Some(watcher) = watcher {
watcher.watch_root(&filter)
}
log::debug!("loading {} ...", filter.root.as_path().display());
let files = load_root(filter.as_ref());
log::debug!("... loaded {}", filter.root.as_path().display());
Some(TaskResult::BulkLoadRoot { root, files })
}
}
}
fn load_root(filter: &RootFilter) -> Vec<(RelativePathBuf, String)> {
let mut res = Vec::new();
for entry in WalkDir::new(&filter.root)
.into_iter()
.filter_entry(filter.entry_filter())
{
let entry = match entry {
Ok(entry) => entry,
Err(e) => {
log::warn!("watcher error: {}", e);
continue;
}
};
if !entry.file_type().is_file() {
continue;
}
let path = entry.path();
let text = match fs::read_to_string(path) {
Ok(text) => text,
Err(e) => {
log::warn!("watcher error: {}", e);
continue;
}
};
let path = RelativePathBuf::from_path(path.strip_prefix(&filter.root).unwrap()).unwrap();
res.push((path.to_owned(), text))
}
res
}
|