11//! JWalk-based parallel directory scanner.
22
33use std:: collections:: HashMap ;
4- use std:: os:: unix:: fs:: MetadataExt ;
54use std:: path:: { Path , PathBuf } ;
65use std:: sync:: atomic:: { AtomicU64 , Ordering } ;
76use std:: sync:: Arc ;
87use std:: time:: Instant ;
98
9+ #[ cfg( unix) ]
10+ use std:: os:: unix:: fs:: MetadataExt ;
11+
1012use compact_str:: CompactString ;
1113use jwalk:: { Parallelism , WalkDir } ;
1214use tokio:: sync:: broadcast;
@@ -48,7 +50,7 @@ impl JwalkScanner {
4850
4951 // Get root device for cross-filesystem detection
5052 let root_metadata = std:: fs:: metadata ( & root_path) . map_err ( |e| ScanError :: io ( & root_path, e) ) ?;
51- let root_device = root_metadata . dev ( ) ;
53+ let root_device = get_dev ( & root_metadata ) ;
5254
5355 // Set up tracking
5456 let inode_tracker = InodeTracker :: new ( ) ;
@@ -136,7 +138,7 @@ impl JwalkScanner {
136138 } ;
137139
138140 // Check cross-filesystem
139- if !config. cross_filesystems && metadata . dev ( ) != root_device {
141+ if !config. cross_filesystems && get_dev ( & metadata ) != root_device {
140142 continue ;
141143 }
142144
@@ -162,7 +164,7 @@ impl JwalkScanner {
162164 metadata. accessed ( ) . ok ( ) ,
163165 metadata. created ( ) . ok ( ) ,
164166 ) ,
165- inode : Some ( InodeInfo :: new ( metadata . ino ( ) , metadata . dev ( ) ) ) ,
167+ inode : Some ( InodeInfo :: new ( get_ino ( & metadata ) , get_dev ( & metadata ) ) ) ,
166168 } ;
167169
168170 entries_by_parent
@@ -172,19 +174,19 @@ impl JwalkScanner {
172174 }
173175 } else if file_type. is_file ( ) {
174176 // Check for hardlinks
175- let inode_info = InodeInfo :: new ( metadata . ino ( ) , metadata . dev ( ) ) ;
177+ let inode_info = InodeInfo :: new ( get_ino ( & metadata ) , get_dev ( & metadata ) ) ;
176178 let size = if config. apparent_size {
177179 metadata. len ( )
178180 } else {
179181 // Only count size for first hardlink
180- if metadata . nlink ( ) > 1 && !inode_tracker. track ( inode_info) {
182+ if get_nlink ( & metadata ) > 1 && !inode_tracker. track ( inode_info) {
181183 0 // Already counted this inode
182184 } else {
183185 metadata. len ( )
184186 }
185187 } ;
186188
187- let blocks = metadata . blocks ( ) ;
189+ let blocks = get_blocks ( & metadata ) ;
188190
189191 stats. record_file (
190192 path. clone ( ) ,
@@ -407,6 +409,53 @@ fn is_executable(_metadata: &std::fs::Metadata) -> bool {
407409 false
408410}
409411
412+ // Cross-platform metadata helpers
413+
414+ /// Get the device ID from metadata.
415+ #[ cfg( unix) ]
416+ fn get_dev ( metadata : & std:: fs:: Metadata ) -> u64 {
417+ metadata. dev ( )
418+ }
419+
420+ #[ cfg( not( unix) ) ]
421+ fn get_dev ( _metadata : & std:: fs:: Metadata ) -> u64 {
422+ 0 // Windows doesn't have device IDs in the same way
423+ }
424+
425+ /// Get the inode number from metadata.
426+ #[ cfg( unix) ]
427+ fn get_ino ( metadata : & std:: fs:: Metadata ) -> u64 {
428+ metadata. ino ( )
429+ }
430+
431+ #[ cfg( not( unix) ) ]
432+ fn get_ino ( _metadata : & std:: fs:: Metadata ) -> u64 {
433+ 0 // Windows doesn't have inodes
434+ }
435+
436+ /// Get the number of hard links from metadata.
437+ #[ cfg( unix) ]
438+ fn get_nlink ( metadata : & std:: fs:: Metadata ) -> u64 {
439+ metadata. nlink ( )
440+ }
441+
442+ #[ cfg( not( unix) ) ]
443+ fn get_nlink ( _metadata : & std:: fs:: Metadata ) -> u64 {
444+ 1 // Assume single link on Windows
445+ }
446+
447+ /// Get the number of 512-byte blocks from metadata.
448+ #[ cfg( unix) ]
449+ fn get_blocks ( metadata : & std:: fs:: Metadata ) -> u64 {
450+ metadata. blocks ( )
451+ }
452+
453+ #[ cfg( not( unix) ) ]
454+ fn get_blocks ( metadata : & std:: fs:: Metadata ) -> u64 {
455+ // Estimate blocks from file size (512-byte blocks, rounded up)
456+ ( metadata. len ( ) + 511 ) / 512
457+ }
458+
410459#[ cfg( test) ]
411460mod tests {
412461 use super :: * ;
0 commit comments