1
- use std:: {
2
- fmt, fs,
3
- path:: { Path , PathBuf } ,
4
- sync:: Arc ,
5
- thread,
6
- } ;
1
+ use std:: { fs, sync:: Arc , thread} ;
7
2
8
3
use crossbeam_channel:: { Receiver , Sender } ;
9
- use parking_lot:: Mutex ;
10
4
use relative_path:: RelativePathBuf ;
11
5
use thread_worker:: WorkerHandle ;
12
6
use walkdir:: WalkDir ;
13
7
14
8
mod watcher;
15
9
use watcher:: Watcher ;
16
- pub use watcher:: WatcherChange ;
17
10
18
- use crate :: { RootFilter , VfsRoot } ;
11
+ use crate :: { RootFilter , Roots , VfsRoot } ;
19
12
20
13
pub ( crate ) enum Task {
21
14
AddRoot {
22
15
root : VfsRoot ,
23
- path : PathBuf ,
24
- root_filter : Arc < RootFilter > ,
25
- nested_roots : Vec < PathBuf > ,
26
- } ,
27
- /// this variant should only be created by the watcher
28
- HandleChange ( WatcherChange ) ,
29
- LoadChange ( WatcherChange ) ,
30
- Watch {
31
- dir : PathBuf ,
32
- root_filter : Arc < RootFilter > ,
16
+ filter : Arc < RootFilter > ,
33
17
} ,
34
18
}
35
19
36
20
#[ derive( Debug ) ]
37
- pub struct AddRootResult {
38
- pub ( crate ) root : VfsRoot ,
39
- pub ( crate ) files : Vec < ( RelativePathBuf , String ) > ,
40
- }
41
-
42
- #[ derive( Debug ) ]
43
- pub enum WatcherChangeData {
44
- Create { path : PathBuf , text : String } ,
45
- Write { path : PathBuf , text : String } ,
46
- Remove { path : PathBuf } ,
47
- }
48
-
49
21
pub enum TaskResult {
50
- AddRoot ( AddRootResult ) ,
51
- HandleChange ( WatcherChange ) ,
52
- LoadChange ( WatcherChangeData ) ,
53
- }
54
-
55
- impl fmt:: Debug for TaskResult {
56
- fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
57
- match self {
58
- TaskResult :: AddRoot ( ..) => f. write_str ( "TaskResult::AddRoot(..)" ) ,
59
- TaskResult :: HandleChange ( c) => write ! ( f, "TaskResult::HandleChange({:?})" , c) ,
60
- TaskResult :: LoadChange ( c) => write ! ( f, "TaskResult::LoadChange({:?})" , c) ,
61
- }
62
- }
22
+ BulkLoadRoot {
23
+ root : VfsRoot ,
24
+ files : Vec < ( RelativePathBuf , String ) > ,
25
+ } ,
26
+ AddSingleFile {
27
+ root : VfsRoot ,
28
+ path : RelativePathBuf ,
29
+ text : String ,
30
+ } ,
31
+ ChangeSingleFile {
32
+ root : VfsRoot ,
33
+ path : RelativePathBuf ,
34
+ text : String ,
35
+ } ,
36
+ RemoveSingleFile {
37
+ root : VfsRoot ,
38
+ path : RelativePathBuf ,
39
+ } ,
63
40
}
64
41
65
42
pub ( crate ) struct Worker {
66
43
worker : thread_worker:: Worker < Task , TaskResult > ,
67
44
worker_handle : WorkerHandle ,
68
- watcher : Arc < Mutex < Option < Watcher > > > ,
69
45
}
70
46
71
47
impl Worker {
72
- pub ( crate ) fn start ( ) -> Worker {
73
- let watcher = Arc :: new ( Mutex :: new ( None ) ) ;
74
- let watcher_clone = watcher. clone ( ) ;
48
+ pub ( crate ) fn start ( roots : Arc < Roots > ) -> Worker {
75
49
let ( worker, worker_handle) =
76
50
thread_worker:: spawn ( "vfs" , 128 , move |input_receiver, output_sender| {
77
- input_receiver
51
+ let mut watcher = match Watcher :: start ( roots, output_sender. clone ( ) ) {
52
+ Ok ( w) => Some ( w) ,
53
+ Err ( e) => {
54
+ log:: error!( "could not start watcher: {}" , e) ;
55
+ None
56
+ }
57
+ } ;
58
+ let res = input_receiver
78
59
. into_iter ( )
79
- . filter_map ( |t| handle_task ( t, & watcher_clone) )
80
- . try_for_each ( |it| output_sender. send ( it) )
81
- . unwrap ( )
60
+ . filter_map ( |t| handle_task ( t, & mut watcher) )
61
+ . try_for_each ( |it| output_sender. send ( it) ) ;
62
+ if let Some ( watcher) = watcher {
63
+ let _ = watcher. shutdown ( ) ;
64
+ }
65
+ res. unwrap ( )
82
66
} ) ;
83
- match Watcher :: start ( worker. inp . clone ( ) ) {
84
- Ok ( w) => {
85
- watcher. lock ( ) . replace ( w) ;
86
- }
87
- Err ( e) => log:: error!( "could not start watcher: {}" , e) ,
88
- } ;
89
67
Worker {
90
68
worker,
91
69
worker_handle,
92
- watcher,
93
70
}
94
71
}
95
72
@@ -102,72 +79,31 @@ impl Worker {
102
79
}
103
80
104
81
pub ( crate ) fn shutdown ( self ) -> thread:: Result < ( ) > {
105
- if let Some ( watcher) = self . watcher . lock ( ) . take ( ) {
106
- let _ = watcher. shutdown ( ) ;
107
- }
108
82
let _ = self . worker . shutdown ( ) ;
109
83
self . worker_handle . shutdown ( )
110
84
}
111
85
}
112
86
113
- fn watch (
114
- watcher : & Arc < Mutex < Option < Watcher > > > ,
115
- dir : & Path ,
116
- filter_entry : & RootFilter ,
117
- emit_for_existing : bool ,
118
- ) {
119
- if let Some ( watcher) = watcher. lock ( ) . as_mut ( ) {
120
- watcher. watch_recursive ( dir, filter_entry, emit_for_existing)
121
- }
122
- }
123
-
124
- fn handle_task ( task : Task , watcher : & Arc < Mutex < Option < Watcher > > > ) -> Option < TaskResult > {
87
+ fn handle_task ( task : Task , watcher : & mut Option < Watcher > ) -> Option < TaskResult > {
125
88
match task {
126
- Task :: AddRoot {
127
- root,
128
- path,
129
- root_filter,
130
- nested_roots,
131
- } => {
132
- watch ( watcher, & path, root_filter. as_ref ( ) , false ) ;
133
- log:: debug!( "loading {} ..." , path. as_path( ) . display( ) ) ;
134
- let files = load_root (
135
- path. as_path ( ) ,
136
- root_filter. as_ref ( ) ,
137
- nested_roots. as_slice ( ) ,
138
- ) ;
139
- log:: debug!( "... loaded {}" , path. as_path( ) . display( ) ) ;
140
- Some ( TaskResult :: AddRoot ( AddRootResult { root, files } ) )
141
- }
142
- Task :: HandleChange ( change) => {
143
- // forward as is because Vfs has to decide if we should load it
144
- Some ( TaskResult :: HandleChange ( change) )
145
- }
146
- Task :: LoadChange ( change) => {
147
- log:: debug!( "loading {:?} ..." , change) ;
148
- load_change ( change) . map ( TaskResult :: LoadChange )
149
- }
150
- Task :: Watch { dir, root_filter } => {
151
- watch ( watcher, & dir, root_filter. as_ref ( ) , true ) ;
152
- None
89
+ Task :: AddRoot { root, filter } => {
90
+ if let Some ( watcher) = watcher {
91
+ watcher. watch_root ( & filter)
92
+ }
93
+ log:: debug!( "loading {} ..." , filter. root. as_path( ) . display( ) ) ;
94
+ let files = load_root ( filter. as_ref ( ) ) ;
95
+ log:: debug!( "... loaded {}" , filter. root. as_path( ) . display( ) ) ;
96
+ Some ( TaskResult :: BulkLoadRoot { root, files } )
153
97
}
154
98
}
155
99
}
156
100
157
- fn load_root (
158
- root : & Path ,
159
- root_filter : & RootFilter ,
160
- nested_roots : & [ PathBuf ] ,
161
- ) -> Vec < ( RelativePathBuf , String ) > {
101
+ fn load_root ( filter : & RootFilter ) -> Vec < ( RelativePathBuf , String ) > {
162
102
let mut res = Vec :: new ( ) ;
163
- for entry in WalkDir :: new ( root) . into_iter ( ) . filter_entry ( |entry| {
164
- if entry. file_type ( ) . is_dir ( ) && nested_roots. iter ( ) . any ( |it| it == entry. path ( ) ) {
165
- // do not load files of a nested root
166
- false
167
- } else {
168
- root_filter. can_contain ( entry. path ( ) ) . is_some ( )
169
- }
170
- } ) {
103
+ for entry in WalkDir :: new ( & filter. root )
104
+ . into_iter ( )
105
+ . filter_entry ( filter. entry_filter ( ) )
106
+ {
171
107
let entry = match entry {
172
108
Ok ( entry) => entry,
173
109
Err ( e) => {
@@ -186,42 +122,8 @@ fn load_root(
186
122
continue ;
187
123
}
188
124
} ;
189
- let path = RelativePathBuf :: from_path ( path. strip_prefix ( root) . unwrap ( ) ) . unwrap ( ) ;
125
+ let path = RelativePathBuf :: from_path ( path. strip_prefix ( & filter . root ) . unwrap ( ) ) . unwrap ( ) ;
190
126
res. push ( ( path. to_owned ( ) , text) )
191
127
}
192
128
res
193
129
}
194
-
195
- fn load_change ( change : WatcherChange ) -> Option < WatcherChangeData > {
196
- let data = match change {
197
- WatcherChange :: Create ( path) => {
198
- if path. is_dir ( ) {
199
- return None ;
200
- }
201
- let text = match fs:: read_to_string ( & path) {
202
- Ok ( text) => text,
203
- Err ( e) => {
204
- log:: warn!( "watcher error \" {}\" : {}" , path. display( ) , e) ;
205
- return None ;
206
- }
207
- } ;
208
- WatcherChangeData :: Create { path, text }
209
- }
210
- WatcherChange :: Write ( path) => {
211
- let text = match fs:: read_to_string ( & path) {
212
- Ok ( text) => text,
213
- Err ( e) => {
214
- log:: warn!( "watcher error \" {}\" : {}" , path. display( ) , e) ;
215
- return None ;
216
- }
217
- } ;
218
- WatcherChangeData :: Write { path, text }
219
- }
220
- WatcherChange :: Remove ( path) => WatcherChangeData :: Remove { path } ,
221
- WatcherChange :: Rescan => {
222
- // this should be handled by Vfs::handle_task
223
- return None ;
224
- }
225
- } ;
226
- Some ( data)
227
- }
0 commit comments