@@ -9,6 +9,7 @@ use glob::optimize_patterns;
9
9
use glob_match:: glob_match;
10
10
use paths:: Path ;
11
11
use rayon:: prelude:: * ;
12
+ use scanner:: allowed_paths:: read_dir;
12
13
use std:: fs;
13
14
use std:: path:: PathBuf ;
14
15
use std:: sync;
@@ -77,6 +78,9 @@ pub struct Scanner {
77
78
/// All files that we have to scan
78
79
files : Vec < PathBuf > ,
79
80
81
+ /// All directories, sub-directories, etc… we saw during source detection
82
+ dirs : Vec < PathBuf > ,
83
+
80
84
/// All generated globs
81
85
globs : Vec < GlobEntry > ,
82
86
@@ -98,7 +102,7 @@ impl Scanner {
98
102
pub fn scan ( & mut self ) -> Vec < String > {
99
103
init_tracing ( ) ;
100
104
self . prepare ( ) ;
101
-
105
+ self . check_for_new_files ( ) ;
102
106
self . compute_candidates ( ) ;
103
107
104
108
let mut candidates: Vec < String > = self . candidates . clone ( ) . into_iter ( ) . collect ( ) ;
@@ -213,6 +217,62 @@ impl Scanner {
213
217
self . ready = true ;
214
218
}
215
219
220
+ #[ tracing:: instrument( skip_all) ]
221
+ fn check_for_new_files ( & mut self ) {
222
+ let mut modified_dirs: Vec < PathBuf > = vec ! [ ] ;
223
+
224
+ // Check all directories to see if they were modified
225
+ for path in & self . dirs {
226
+ let current_time = fs:: metadata ( path)
227
+ . and_then ( |m| m. modified ( ) )
228
+ . unwrap_or ( SystemTime :: now ( ) ) ;
229
+
230
+ let previous_time = self . mtimes . insert ( path. clone ( ) , current_time) ;
231
+
232
+ let should_scan = match previous_time {
233
+ // Time has changed, so we need to re-scan the file
234
+ Some ( prev) if prev != current_time => true ,
235
+
236
+ // File was in the cache, no need to re-scan
237
+ Some ( _) => false ,
238
+
239
+ // File didn't exist before, so we need to scan it
240
+ None => true ,
241
+ } ;
242
+
243
+ if should_scan {
244
+ modified_dirs. push ( path. clone ( ) ) ;
245
+ }
246
+ }
247
+
248
+ // Scan all modified directories for their immediate files
249
+ let mut known = FxHashSet :: from_iter ( self . files . iter ( ) . chain ( self . dirs . iter ( ) ) . cloned ( ) ) ;
250
+
251
+ while !modified_dirs. is_empty ( ) {
252
+ let new_entries = modified_dirs
253
+ . iter ( )
254
+ . flat_map ( |dir| read_dir ( dir, Some ( 1 ) ) )
255
+ . map ( |entry| entry. path ( ) . to_owned ( ) )
256
+ . filter ( |path| !known. contains ( path) )
257
+ . collect :: < Vec < _ > > ( ) ;
258
+
259
+ modified_dirs. clear ( ) ;
260
+
261
+ for path in new_entries {
262
+ if path. is_file ( ) {
263
+ known. insert ( path. clone ( ) ) ;
264
+ self . files . push ( path) ;
265
+ } else if path. is_dir ( ) {
266
+ known. insert ( path. clone ( ) ) ;
267
+ self . dirs . push ( path. clone ( ) ) ;
268
+
269
+ // Recursively scan the new directory for files
270
+ modified_dirs. push ( path) ;
271
+ }
272
+ }
273
+ }
274
+ }
275
+
216
276
#[ tracing:: instrument( skip_all) ]
217
277
fn scan_sources ( & mut self ) {
218
278
let Some ( sources) = & self . sources else {
@@ -282,9 +342,10 @@ impl Scanner {
282
342
// Detect all files/folders in the directory
283
343
let detect_sources = DetectSources :: new ( path) ;
284
344
285
- let ( files, globs) = detect_sources. detect ( ) ;
345
+ let ( files, globs, dirs ) = detect_sources. detect ( ) ;
286
346
self . files . extend ( files) ;
287
347
self . globs . extend ( globs) ;
348
+ self . dirs . extend ( dirs) ;
288
349
}
289
350
290
351
// Turn `Vec<&GlobEntry>` in `Vec<GlobEntry>`
0 commit comments