@@ -4,7 +4,7 @@ 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;
99use ignore:: { WalkBuilder , WalkParallel , WalkState } ;
1010use rayon:: iter:: { IntoParallelIterator , ParallelIterator } ;
@@ -51,53 +51,75 @@ 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 ) ;
5756 let walk_parallel: WalkParallel = builder. build_parallel ( ) ;
5857
59- let collected = Arc :: new ( Mutex :: new ( Vec :: with_capacity ( INITIAL_VECTOR_CAPACITY ) ) ) ;
60- let collected_for_threads = Arc :: clone ( & collected) ;
58+ let collected_entry_types = Arc :: new ( Mutex :: new ( Vec :: with_capacity ( INITIAL_VECTOR_CAPACITY ) ) ) ;
59+ let collected_for_threads = Arc :: clone ( & collected_entry_types) ;
60+ let error_holder: Arc < Mutex < Option < Report < Error > > > > = Arc :: new ( Mutex :: new ( None ) ) ;
61+ let error_holder_for_threads = Arc :: clone ( & error_holder) ;
62+
63+ let this: & ProjectBuilder < ' a > = self ;
6164
6265 walk_parallel. run ( move || {
6366 let collected = Arc :: clone ( & collected_for_threads) ;
67+ let error_holder = Arc :: clone ( & error_holder_for_threads) ;
6468 Box :: new ( move |res| {
6569 if let Ok ( entry) = res {
66- if let Ok ( mut v) = collected. lock ( ) {
67- v. push ( entry) ;
70+ match this. build_entry_type ( entry) {
71+ Ok ( entry_type) => {
72+ if let Ok ( mut v) = collected. lock ( ) {
73+ v. push ( entry_type) ;
74+ }
75+ }
76+ Err ( report) => {
77+ if let Ok ( mut slot) = error_holder. lock ( ) {
78+ if slot. is_none ( ) {
79+ * slot = Some ( report) ;
80+ }
81+ }
82+ }
6883 }
6984 }
7085 WalkState :: Continue
7186 } )
7287 } ) ;
7388
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
89+ // Take ownership of the collected entry types
90+ let entry_types = match Arc :: try_unwrap ( collected_entry_types) {
7791 Ok ( mutex) => match mutex. into_inner ( ) {
78- // Mutex not poisoned
7992 Ok ( entries) => entries,
80- // Recover entries even if the mutex was poisoned
8193 Err ( poisoned) => poisoned. into_inner ( ) ,
8294 } ,
83- // There are still other Arc references; lock and take the contents
8495 Err ( arc) => match arc. lock ( ) {
8596 Ok ( mut guard) => std:: mem:: take ( & mut * guard) ,
86- // Recover guard even if poisoned, then take contents
8797 Err ( poisoned) => {
8898 let mut guard = poisoned. into_inner ( ) ;
8999 std:: mem:: take ( & mut * guard)
90100 }
91101 } ,
92102 } ;
93- for entry in collected_entries {
94- entry_types. push ( self . build_entry_type ( entry) ?) ;
103+
104+ // If any error occurred while building entry types, return it
105+ let maybe_error = match Arc :: try_unwrap ( error_holder) {
106+ Ok ( mutex) => match mutex. into_inner ( ) {
107+ Ok ( err_opt) => err_opt,
108+ Err ( poisoned) => poisoned. into_inner ( ) ,
109+ } ,
110+ Err ( arc) => match arc. lock ( ) {
111+ Ok ( mut guard) => guard. take ( ) ,
112+ Err ( poisoned) => poisoned. into_inner ( ) . take ( ) ,
113+ } ,
114+ } ;
115+ if let Some ( report) = maybe_error {
116+ return Err ( report) ;
95117 }
96118
97119 self . build_project_from_entry_types ( entry_types)
98120 }
99121
100- fn build_entry_type ( & mut self , entry : ignore:: DirEntry ) -> Result < EntryType , Error > {
122+ fn build_entry_type ( & self , entry : ignore:: DirEntry ) -> Result < EntryType , Error > {
101123 let absolute_path = entry. path ( ) ;
102124
103125 let is_dir = entry. file_type ( ) . ok_or ( Error :: Io ) . change_context ( Error :: Io ) ?. is_dir ( ) ;
0 commit comments