@@ -171,12 +171,14 @@ impl FunChildren {
171171 let last_child_res = if let Some ( stdout) = last_child. stdout {
172172 let mut stdout: Box < dyn Read > = Box :: new ( stdout) ;
173173 f ( & mut stdout) ;
174+ // The provided function may have left some of stdout unread.
175+ // Continue reading stdout on its behalf, until the child exits.
174176 let mut buf = vec ! [ 0 ; 65536 ] ;
175- let status : Box < dyn ChildOutcome > = loop {
177+ let outcome : Box < dyn ChildOutcome > = loop {
176178 match last_child. handle {
177179 CmdChildHandle :: Proc ( ref mut child) => {
178- if let Ok ( Some ( status ) ) = child. try_wait ( ) {
179- break Box :: new ( status ) ;
180+ if let Some ( result ) = child. try_wait ( ) . transpose ( ) {
181+ break Box :: new ( ProcWaitOutcome :: from ( result ) ) ;
180182 }
181183 }
182184 CmdChildHandle :: Thread ( ref mut join_handle) => {
@@ -194,16 +196,7 @@ impl FunChildren {
194196 }
195197 let _ = stdout. read ( & mut buf) ;
196198 } ;
197- if status. success ( ) {
198- Ok ( ( ) )
199- } else {
200- Err ( CmdChildHandle :: outcome_to_io_error (
201- & * status,
202- & last_child. cmd ,
203- & last_child. file ,
204- last_child. line ,
205- ) )
206- }
199+ outcome. to_io_result ( & last_child. cmd , & last_child. file , last_child. line )
207200 } else {
208201 last_child. wait ( true )
209202 } ;
@@ -329,6 +322,29 @@ pub(crate) enum CmdChildHandle {
329322 SyncFn ,
330323}
331324
325+ #[ derive( Debug ) ]
326+ struct ProcWaitOutcome ( std:: io:: Result < ExitStatus > ) ;
327+ impl From < std:: io:: Result < ExitStatus > > for ProcWaitOutcome {
328+ fn from ( result : std:: io:: Result < ExitStatus > ) -> Self {
329+ Self ( result)
330+ }
331+ }
332+ impl Display for ProcWaitOutcome {
333+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
334+ match & self . 0 {
335+ Ok ( status) => {
336+ if status. success ( ) {
337+ write ! ( f, "Command process succeeded" )
338+ } else if let Some ( code) = status. code ( ) {
339+ write ! ( f, "Command process exited normally with status code {code}" )
340+ } else {
341+ write ! ( f, "Command process exited abnormally: {status}" )
342+ }
343+ }
344+ Err ( error) => write ! ( f, "Failed to wait for command process: {error:?}" ) ,
345+ }
346+ }
347+ }
332348#[ derive( Debug ) ]
333349enum ThreadJoinOutcome {
334350 Ok ,
@@ -362,86 +378,47 @@ impl Display for SyncFnOutcome {
362378}
363379trait ChildOutcome : Display {
364380 fn success ( & self ) -> bool ;
365- fn code ( & self ) -> Option < i32 > ;
381+ fn to_io_result ( & self , cmd : & str , file : & str , line : u32 ) -> std:: io:: Result < ( ) > {
382+ if self . success ( ) {
383+ Ok ( ( ) )
384+ } else {
385+ Err ( Error :: new (
386+ ErrorKind :: Other ,
387+ format ! ( "Running [{cmd}] exited with error; {self} at {file}:{line}" ) ,
388+ ) )
389+ }
390+ }
366391}
367- impl ChildOutcome for ExitStatus {
392+ impl ChildOutcome for ProcWaitOutcome {
368393 fn success ( & self ) -> bool {
369- self . success ( )
370- }
371- fn code ( & self ) -> Option < i32 > {
372- self . code ( )
394+ self . 0 . as_ref ( ) . is_ok_and ( |status| status. success ( ) )
373395 }
374396}
375397impl ChildOutcome for ThreadJoinOutcome {
376398 fn success ( & self ) -> bool {
377399 matches ! ( self , Self :: Ok )
378400 }
379- fn code ( & self ) -> Option < i32 > {
380- None
381- }
382401}
383402impl ChildOutcome for SyncFnOutcome {
384403 fn success ( & self ) -> bool {
385404 true
386405 }
387- fn code ( & self ) -> Option < i32 > {
388- None
389- }
390406}
391407
392408impl CmdChildHandle {
393409 fn wait ( self , cmd : & str , file : & str , line : u32 ) -> CmdResult {
394- match self {
395- CmdChildHandle :: Proc ( mut proc) => {
396- let status = proc. wait ( ) ;
397- match status {
398- Err ( e) => return Err ( process:: new_cmd_io_error ( & e, cmd, file, line) ) ,
399- Ok ( status) => {
400- if !status. success ( ) {
401- return Err ( Self :: outcome_to_io_error ( & status, cmd, file, line) ) ;
402- }
403- }
404- }
405- }
410+ let outcome: Box < dyn ChildOutcome > = match self {
411+ CmdChildHandle :: Proc ( mut proc) => Box :: new ( ProcWaitOutcome :: from ( proc. wait ( ) ) ) ,
406412 CmdChildHandle :: Thread ( mut thread) => {
407413 if let Some ( thread) = thread. take ( ) {
408- let status = thread. join ( ) ;
409- match status {
410- Ok ( result) => {
411- if let Err ( e) = result {
412- return Err ( process:: new_cmd_io_error ( & e, cmd, file, line) ) ;
413- }
414- }
415- Err ( e) => {
416- return Err ( Error :: new (
417- ErrorKind :: Other ,
418- format ! (
419- "Running [{cmd}] thread joined with error: {e:?} at {file}:{line}"
420- ) ,
421- ) )
422- }
423- }
414+ Box :: new ( ThreadJoinOutcome :: from ( thread. join ( ) ) )
415+ } else {
416+ unreachable ! ( )
424417 }
425418 }
426- CmdChildHandle :: SyncFn => { }
427- }
428- Ok ( ( ) )
429- }
430-
431- fn outcome_to_io_error ( outcome : & dyn ChildOutcome , cmd : & str , file : & str , line : u32 ) -> Error {
432- if let Some ( code) = outcome. code ( ) {
433- Error :: new (
434- ErrorKind :: Other ,
435- format ! ( "Running [{cmd}] exited with error; status code: {code} at {file}:{line}" ) ,
436- )
437- } else {
438- Error :: new (
439- ErrorKind :: Other ,
440- format ! (
441- "Running [{cmd}] exited with error; terminated by {outcome} at {file}:{line}"
442- ) ,
443- )
444- }
419+ CmdChildHandle :: SyncFn => return Ok ( ( ) ) ,
420+ } ;
421+ outcome. to_io_result ( cmd, file, line)
445422 }
446423
447424 fn kill ( self , cmd : & str , file : & str , line : u32 ) -> CmdResult {
0 commit comments