@@ -171,12 +171,14 @@ impl FunChildren {
171
171
let last_child_res = if let Some ( stdout) = last_child. stdout {
172
172
let mut stdout: Box < dyn Read > = Box :: new ( stdout) ;
173
173
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.
174
176
let mut buf = vec ! [ 0 ; 65536 ] ;
175
- let status : Box < dyn ChildOutcome > = loop {
177
+ let outcome : Box < dyn ChildOutcome > = loop {
176
178
match last_child. handle {
177
179
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 ) ) ;
180
182
}
181
183
}
182
184
CmdChildHandle :: Thread ( ref mut join_handle) => {
@@ -194,16 +196,7 @@ impl FunChildren {
194
196
}
195
197
let _ = stdout. read ( & mut buf) ;
196
198
} ;
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 )
207
200
} else {
208
201
last_child. wait ( true )
209
202
} ;
@@ -329,6 +322,29 @@ pub(crate) enum CmdChildHandle {
329
322
SyncFn ,
330
323
}
331
324
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
+ }
332
348
#[ derive( Debug ) ]
333
349
enum ThreadJoinOutcome {
334
350
Ok ,
@@ -362,86 +378,47 @@ impl Display for SyncFnOutcome {
362
378
}
363
379
trait ChildOutcome : Display {
364
380
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
+ }
366
391
}
367
- impl ChildOutcome for ExitStatus {
392
+ impl ChildOutcome for ProcWaitOutcome {
368
393
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 ( ) )
373
395
}
374
396
}
375
397
impl ChildOutcome for ThreadJoinOutcome {
376
398
fn success ( & self ) -> bool {
377
399
matches ! ( self , Self :: Ok )
378
400
}
379
- fn code ( & self ) -> Option < i32 > {
380
- None
381
- }
382
401
}
383
402
impl ChildOutcome for SyncFnOutcome {
384
403
fn success ( & self ) -> bool {
385
404
true
386
405
}
387
- fn code ( & self ) -> Option < i32 > {
388
- None
389
- }
390
406
}
391
407
392
408
impl CmdChildHandle {
393
409
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 ( ) ) ) ,
406
412
CmdChildHandle :: Thread ( mut thread) => {
407
413
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 ! ( )
424
417
}
425
418
}
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)
445
422
}
446
423
447
424
fn kill ( self , cmd : & str , file : & str , line : u32 ) -> CmdResult {
0 commit comments