11use crate :: { info, warn} ;
22use crate :: { process, CmdResult , FunResult } ;
33use os_pipe:: PipeReader ;
4+ use std:: any:: Any ;
5+ use std:: fmt:: Display ;
46use std:: io:: { BufRead , BufReader , Error , ErrorKind , Read , Result } ;
57use std:: process:: { Child , ExitStatus } ;
68use std:: thread:: JoinHandle ;
@@ -170,19 +172,33 @@ impl FunChildren {
170172 let mut stdout: Box < dyn Read > = Box :: new ( stdout) ;
171173 f ( & mut stdout) ;
172174 let mut buf = vec ! [ 0 ; 65536 ] ;
173- let status = loop {
174- if let CmdChildHandle :: Proc ( child) = & mut last_child. handle {
175- if let Ok ( Some ( status) ) = child. try_wait ( ) {
176- break status;
175+ let status: Box < dyn ChildOutcome > = loop {
176+ match last_child. handle {
177+ CmdChildHandle :: Proc ( ref mut child) => {
178+ if let Ok ( Some ( status) ) = child. try_wait ( ) {
179+ break Box :: new ( status) ;
180+ }
181+ }
182+ CmdChildHandle :: Thread ( ref mut join_handle) => {
183+ if let Some ( handle) = join_handle. take ( ) {
184+ if handle. is_finished ( ) {
185+ break Box :: new ( ThreadJoinOutcome :: from ( handle. join ( ) ) ) ;
186+ } else {
187+ join_handle. replace ( handle) ;
188+ }
189+ }
190+ }
191+ CmdChildHandle :: SyncFn => {
192+ break Box :: new ( SyncFnOutcome ) ;
177193 }
178194 }
179195 let _ = stdout. read ( & mut buf) ;
180196 } ;
181197 if status. success ( ) {
182198 Ok ( ( ) )
183199 } else {
184- Err ( CmdChildHandle :: status_to_io_error (
185- status,
200+ Err ( CmdChildHandle :: outcome_to_io_error (
201+ & * status,
186202 & last_child. cmd ,
187203 & last_child. file ,
188204 last_child. line ,
@@ -309,10 +325,70 @@ impl CmdChild {
309325
310326pub ( crate ) enum CmdChildHandle {
311327 Proc ( Child ) ,
312- Thread ( JoinHandle < CmdResult > ) ,
328+ Thread ( Option < JoinHandle < CmdResult > > ) ,
313329 SyncFn ,
314330}
315331
332+ #[ derive( Debug ) ]
333+ enum ThreadJoinOutcome {
334+ Ok ,
335+ Err ( std:: io:: Error ) ,
336+ Panic ( Box < dyn Any + Send + ' static > ) ,
337+ }
338+ impl From < std:: thread:: Result < CmdResult > > for ThreadJoinOutcome {
339+ fn from ( result : std:: thread:: Result < CmdResult > ) -> Self {
340+ match result {
341+ Ok ( Ok ( ( ) ) ) => Self :: Ok ,
342+ Ok ( Err ( error) ) => Self :: Err ( error) ,
343+ Err ( panic) => Self :: Panic ( panic) ,
344+ }
345+ }
346+ }
347+ impl Display for ThreadJoinOutcome {
348+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
349+ match self {
350+ Self :: Ok => write ! ( f, "Command thread succeeded" ) ,
351+ Self :: Err ( error) => write ! ( f, "Command thread returned error: {error:?}" ) ,
352+ Self :: Panic ( panic) => write ! ( f, "Command thread panicked: {panic:?}" ) ,
353+ }
354+ }
355+ }
356+ #[ derive( Debug ) ]
357+ struct SyncFnOutcome ;
358+ impl Display for SyncFnOutcome {
359+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
360+ write ! ( f, "Command finished" )
361+ }
362+ }
363+ trait ChildOutcome : Display {
364+ fn success ( & self ) -> bool ;
365+ fn code ( & self ) -> Option < i32 > ;
366+ }
367+ impl ChildOutcome for ExitStatus {
368+ fn success ( & self ) -> bool {
369+ self . success ( )
370+ }
371+ fn code ( & self ) -> Option < i32 > {
372+ self . code ( )
373+ }
374+ }
375+ impl ChildOutcome for ThreadJoinOutcome {
376+ fn success ( & self ) -> bool {
377+ matches ! ( self , Self :: Ok )
378+ }
379+ fn code ( & self ) -> Option < i32 > {
380+ None
381+ }
382+ }
383+ impl ChildOutcome for SyncFnOutcome {
384+ fn success ( & self ) -> bool {
385+ true
386+ }
387+ fn code ( & self ) -> Option < i32 > {
388+ None
389+ }
390+ }
391+
316392impl CmdChildHandle {
317393 fn wait ( self , cmd : & str , file : & str , line : u32 ) -> CmdResult {
318394 match self {
@@ -322,26 +398,28 @@ impl CmdChildHandle {
322398 Err ( e) => return Err ( process:: new_cmd_io_error ( & e, cmd, file, line) ) ,
323399 Ok ( status) => {
324400 if !status. success ( ) {
325- return Err ( Self :: status_to_io_error ( status, cmd, file, line) ) ;
401+ return Err ( Self :: outcome_to_io_error ( & status, cmd, file, line) ) ;
326402 }
327403 }
328404 }
329405 }
330- CmdChildHandle :: Thread ( thread) => {
331- let status = thread. join ( ) ;
332- match status {
333- Ok ( result) => {
334- if let Err ( e) = result {
335- return Err ( process:: new_cmd_io_error ( & e, cmd, file, line) ) ;
406+ CmdChildHandle :: Thread ( mut thread) => {
407+ 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+ }
336414 }
337- }
338- Err ( e) => {
339- return Err ( Error :: new (
340- ErrorKind :: Other ,
341- format ! (
415+ Err ( e) => {
416+ return Err ( Error :: new (
417+ ErrorKind :: Other ,
418+ format ! (
342419 "Running [{cmd}] thread joined with error: {e:?} at {file}:{line}"
343420 ) ,
344- ) )
421+ ) )
422+ }
345423 }
346424 }
347425 }
@@ -350,8 +428,8 @@ impl CmdChildHandle {
350428 Ok ( ( ) )
351429 }
352430
353- fn status_to_io_error ( status : ExitStatus , cmd : & str , file : & str , line : u32 ) -> Error {
354- if let Some ( code) = status . code ( ) {
431+ fn outcome_to_io_error ( outcome : & dyn ChildOutcome , cmd : & str , file : & str , line : u32 ) -> Error {
432+ if let Some ( code) = outcome . code ( ) {
355433 Error :: new (
356434 ErrorKind :: Other ,
357435 format ! ( "Running [{cmd}] exited with error; status code: {code} at {file}:{line}" ) ,
@@ -360,7 +438,7 @@ impl CmdChildHandle {
360438 Error :: new (
361439 ErrorKind :: Other ,
362440 format ! (
363- "Running [{cmd}] exited with error; terminated by {status } at {file}:{line}"
441+ "Running [{cmd}] exited with error; terminated by {outcome } at {file}:{line}"
364442 ) ,
365443 )
366444 }
0 commit comments