@@ -4,9 +4,9 @@ use std::{
44 sync:: { Arc , Mutex } ,
55} ;
66
7- use error_stack:: { Result , ResultExt } ;
7+ use error_stack:: { Report , Result , ResultExt } ;
88use fast_glob:: glob_match;
9- use ignore:: { WalkBuilder , WalkParallel , WalkState } ;
9+ use ignore:: { DirEntry , WalkBuilder , WalkParallel , WalkState } ;
1010use rayon:: iter:: { IntoParallelIterator , ParallelIterator } ;
1111use tracing:: { instrument, warn} ;
1212
@@ -51,53 +51,77 @@ impl<'a> ProjectBuilder<'a> {
5151
5252 #[ instrument( level = "debug" , skip_all) ]
5353 pub fn build ( & mut self ) -> Result < Project , Error > {
54- let mut entry_types = Vec :: with_capacity ( INITIAL_VECTOR_CAPACITY ) ;
5554 let mut builder = WalkBuilder :: new ( & self . base_path ) ;
5655 builder. hidden ( false ) ;
56+ builder. follow_links ( false ) ;
57+ // Prune traversal early: skip heavy and irrelevant directories
58+ let ignore_globs = self . config . ignore_globs . clone ( ) ;
59+ let base_path = self . base_path . clone ( ) ;
60+
61+ builder. filter_entry ( move |entry : & DirEntry | {
62+ if let Ok ( relative_path) = entry. path ( ) . strip_prefix ( & base_path) {
63+ if let Some ( relative_path_str) = relative_path. to_str ( ) {
64+ if crate :: ignore:: matches_path ( & ignore_globs, relative_path_str) {
65+ return false ;
66+ }
67+ }
68+ }
69+
70+ true
71+ } ) ;
72+
5773 let walk_parallel: WalkParallel = builder. build_parallel ( ) ;
5874
59- let collected = Arc :: new ( Mutex :: new ( Vec :: with_capacity ( INITIAL_VECTOR_CAPACITY ) ) ) ;
60- let collected_for_threads = Arc :: clone ( & collected) ;
75+ let ( tx, rx) = crossbeam_channel:: unbounded :: < EntryType > ( ) ;
76+ let error_holder: Arc < Mutex < Option < Report < Error > > > > = Arc :: new ( Mutex :: new ( None ) ) ;
77+ let error_holder_for_threads = Arc :: clone ( & error_holder) ;
78+
79+ let this: & ProjectBuilder < ' a > = self ;
6180
6281 walk_parallel. run ( move || {
63- let collected = Arc :: clone ( & collected_for_threads) ;
82+ let error_holder = Arc :: clone ( & error_holder_for_threads) ;
83+ let tx = tx. clone ( ) ;
6484 Box :: new ( move |res| {
6585 if let Ok ( entry) = res {
66- if let Ok ( mut v) = collected. lock ( ) {
67- v. push ( entry) ;
86+ match this. build_entry_type ( entry) {
87+ Ok ( entry_type) => {
88+ let _ = tx. send ( entry_type) ;
89+ }
90+ Err ( report) => {
91+ if let Ok ( mut slot) = error_holder. lock ( ) {
92+ if slot. is_none ( ) {
93+ * slot = Some ( report) ;
94+ }
95+ }
96+ }
6897 }
6998 }
7099 WalkState :: Continue
71100 } )
72101 } ) ;
73102
74- // Process sequentially with &mut self without panicking on Arc/Mutex unwraps
75- let collected_entries = match Arc :: try_unwrap ( collected) {
76- // We are the sole owner of the Arc
103+ // Take ownership of the collected entry types
104+ let entry_types: Vec < EntryType > = rx. iter ( ) . collect ( ) ;
105+
106+ // If any error occurred while building entry types, return it
107+ let maybe_error = match Arc :: try_unwrap ( error_holder) {
77108 Ok ( mutex) => match mutex. into_inner ( ) {
78- // Mutex not poisoned
79- Ok ( entries) => entries,
80- // Recover entries even if the mutex was poisoned
109+ Ok ( err_opt) => err_opt,
81110 Err ( poisoned) => poisoned. into_inner ( ) ,
82111 } ,
83- // There are still other Arc references; lock and take the contents
84112 Err ( arc) => match arc. lock ( ) {
85- Ok ( mut guard) => std:: mem:: take ( & mut * guard) ,
86- // Recover guard even if poisoned, then take contents
87- Err ( poisoned) => {
88- let mut guard = poisoned. into_inner ( ) ;
89- std:: mem:: take ( & mut * guard)
90- }
113+ Ok ( mut guard) => guard. take ( ) ,
114+ Err ( poisoned) => poisoned. into_inner ( ) . take ( ) ,
91115 } ,
92116 } ;
93- for entry in collected_entries {
94- entry_types . push ( self . build_entry_type ( entry ) ? ) ;
117+ if let Some ( report ) = maybe_error {
118+ return Err ( report ) ;
95119 }
96120
97121 self . build_project_from_entry_types ( entry_types)
98122 }
99123
100- fn build_entry_type ( & mut self , entry : ignore:: DirEntry ) -> Result < EntryType , Error > {
124+ fn build_entry_type ( & self , entry : ignore:: DirEntry ) -> Result < EntryType , Error > {
101125 let absolute_path = entry. path ( ) ;
102126
103127 let is_dir = entry. file_type ( ) . ok_or ( Error :: Io ) . change_context ( Error :: Io ) ?. is_dir ( ) ;
0 commit comments