1- use crate :: { info, warn } ;
1+ use crate :: info;
22use crate :: { process, CmdResult , FunResult } ;
33use os_pipe:: PipeReader ;
44use std:: io:: { BufRead , BufReader , Error , ErrorKind , Read , Result } ;
@@ -8,7 +8,7 @@ use std::thread::JoinHandle;
88/// Representation of running or exited children processes, connected with pipes
99/// optionally.
1010///
11- /// Calling `spawn!` macro will return `Result<CmdChildren>`
11+ /// Calling [ `spawn!`](../cmd_lib/macro.spawn.html) macro will return `Result<CmdChildren>`
1212pub struct CmdChildren {
1313 children : Vec < CmdChild > ,
1414 ignore_error : bool ,
@@ -70,7 +70,7 @@ impl CmdChildren {
7070/// Representation of running or exited children processes with output, connected with pipes
7171/// optionally.
7272///
73- /// Calling ` spawn_with_output!` macro will return `Result<FunChildren>`
73+ /// Calling [ spawn_with_output!](../cmd_lib/macro.spawn_with_output.html) macro will return `Result<FunChildren>`
7474pub struct FunChildren {
7575 children : Vec < CmdChild > ,
7676 ignore_error : bool ,
@@ -88,17 +88,13 @@ impl FunChildren {
8888 Err ( e)
8989 }
9090 Ok ( output) => {
91- let mut s = String :: from_utf8_lossy ( & output) . to_string ( ) ;
92- if s. ends_with ( '\n' ) {
93- s. pop ( ) ;
94- }
9591 let ret = CmdChildren :: wait_children ( & mut self . children ) ;
9692 if let Err ( e) = ret {
9793 if !self . ignore_error {
9894 return Err ( e) ;
9995 }
10096 }
101- Ok ( s )
97+ Ok ( output )
10298 }
10399 }
104100 }
@@ -107,7 +103,7 @@ impl FunChildren {
107103 /// provided function.
108104 pub fn wait_with_pipe ( & mut self , f : & mut dyn FnMut ( Box < dyn Read > ) ) -> CmdResult {
109105 let child = self . children . pop ( ) . unwrap ( ) ;
110- let polling_stderr = StderrLogging :: new ( & child. cmd , child. stderr ) ;
106+ let stderr_thread = StderrThread :: new ( & child. cmd , child. stderr , false ) ;
111107 match child. handle {
112108 CmdChildHandle :: Proc ( mut proc) => {
113109 if let Some ( stdout) = child. stdout {
@@ -126,10 +122,20 @@ impl FunChildren {
126122 }
127123 }
128124 } ;
129- drop ( polling_stderr ) ;
125+ drop ( stderr_thread ) ;
130126 CmdChildren :: wait_children ( & mut self . children )
131127 }
132128
129+ /// Waits for the children processes to exit completely, returning the command result, stdout
130+ /// read result and stderr read result.
131+ pub fn wait_with_all ( & mut self ) -> ( CmdResult , FunResult , FunResult ) {
132+ // wait for the last child result
133+ let handle = self . children . pop ( ) . unwrap ( ) ;
134+ let wait_all = handle. wait_with_all ( true ) ;
135+ let _ = CmdChildren :: wait_children ( & mut self . children ) ;
136+ wait_all
137+ }
138+
133139 /// Returns the OS-assigned process identifiers associated with these children processes
134140 pub fn pids ( & self ) -> Vec < u32 > {
135141 self . children . iter ( ) . filter_map ( |x| x. pid ( ) ) . collect ( )
@@ -158,8 +164,9 @@ impl CmdChild {
158164 }
159165 }
160166
161- fn wait ( self , is_last : bool ) -> CmdResult {
162- let res = self . handle . wait_with_stderr ( self . stderr , & self . cmd ) ;
167+ fn wait ( mut self , is_last : bool ) -> CmdResult {
168+ let _stderr_thread = StderrThread :: new ( & self . cmd , self . stderr . take ( ) , false ) ;
169+ let res = self . handle . wait ( & self . cmd ) ;
163170 if let Err ( e) = res {
164171 if is_last || process:: pipefail_enabled ( ) {
165172 return Err ( e) ;
@@ -168,27 +175,35 @@ impl CmdChild {
168175 Ok ( ( ) )
169176 }
170177
171- fn wait_with_output ( self , ignore_error : bool ) -> Result < Vec < u8 > > {
172- let buf = {
173- if let Some ( mut out) = self . stdout {
174- let mut buf = vec ! [ ] ;
175- if let Err ( e) = out. read_to_end ( & mut buf) {
176- if !ignore_error {
177- return Err ( process:: new_cmd_io_error ( & e, & self . cmd ) ) ;
178+ fn wait_with_output ( self , ignore_error : bool ) -> FunResult {
179+ let ( res, stdout, _) = self . wait_with_all ( false ) ;
180+ if !ignore_error {
181+ res?;
182+ }
183+ stdout
184+ }
185+
186+ fn wait_with_all ( mut self , capture : bool ) -> ( CmdResult , FunResult , FunResult ) {
187+ let mut stderr_thread = StderrThread :: new ( & self . cmd , self . stderr . take ( ) , capture) ;
188+ let stdout_output = {
189+ if let Some ( mut out) = self . stdout . take ( ) {
190+ let mut s = String :: new ( ) ;
191+ match out. read_to_string ( & mut s) {
192+ Err ( e) => Err ( e) ,
193+ Ok ( _) => {
194+ if s. ends_with ( '\n' ) {
195+ s. pop ( ) ;
196+ }
197+ Ok ( s)
178198 }
179199 }
180- buf
181200 } else {
182- vec ! [ ]
201+ Ok ( "" . into ( ) )
183202 }
184203 } ;
185- let res = self . handle . wait_with_stderr ( self . stderr , & self . cmd ) ;
186- if let Err ( e) = res {
187- if !ignore_error {
188- return Err ( e) ;
189- }
190- }
191- Ok ( buf)
204+ let stderr_output = stderr_thread. join ( ) ;
205+ let res = self . handle . wait ( & self . cmd ) ;
206+ ( res, stdout_output, stderr_output)
192207 }
193208
194209 fn kill ( self ) -> CmdResult {
@@ -207,8 +222,7 @@ pub(crate) enum CmdChildHandle {
207222}
208223
209224impl CmdChildHandle {
210- fn wait_with_stderr ( self , stderr : Option < PipeReader > , cmd : & str ) -> CmdResult {
211- let polling_stderr = StderrLogging :: new ( cmd, stderr) ;
225+ fn wait ( self , cmd : & str ) -> CmdResult {
212226 match self {
213227 CmdChildHandle :: Proc ( mut proc) => {
214228 let status = proc. wait ( ) ;
@@ -242,7 +256,6 @@ impl CmdChildHandle {
242256 }
243257 CmdChildHandle :: SyncFn => { }
244258 }
245- drop ( polling_stderr) ;
246259 Ok ( ( ) )
247260 }
248261
@@ -272,19 +285,31 @@ impl CmdChildHandle {
272285 }
273286}
274287
275- struct StderrLogging {
276- thread : Option < JoinHandle < ( ) > > ,
288+ struct StderrThread {
289+ thread : Option < JoinHandle < String > > ,
277290 cmd : String ,
278291}
279292
280- impl StderrLogging {
281- fn new ( cmd : & str , stderr : Option < PipeReader > ) -> Self {
293+ impl StderrThread {
294+ fn new ( cmd : & str , stderr : Option < PipeReader > , capture : bool ) -> Self {
282295 if let Some ( stderr) = stderr {
283296 let thread = std:: thread:: spawn ( move || {
297+ let mut output = String :: new ( ) ;
284298 BufReader :: new ( stderr)
285299 . lines ( )
286300 . map_while ( Result :: ok)
287- . for_each ( |line| info ! ( "{}" , line) )
301+ . for_each ( |line| {
302+ if !capture {
303+ info ! ( "{line}" ) ;
304+ } else {
305+ output. push_str ( & line) ;
306+ output. push ( '\n' ) ;
307+ }
308+ } ) ;
309+ if output. ends_with ( '\n' ) {
310+ output. pop ( ) ;
311+ }
312+ output
288313 } ) ;
289314 Self {
290315 cmd : cmd. into ( ) ,
@@ -297,14 +322,28 @@ impl StderrLogging {
297322 }
298323 }
299324 }
300- }
301325
302- impl Drop for StderrLogging {
303- fn drop ( & mut self ) {
326+ fn join ( & mut self ) -> FunResult {
304327 if let Some ( thread) = self . thread . take ( ) {
305- if let Err ( e) = thread. join ( ) {
306- warn ! ( "[{}] logging thread exited with error: {:?}" , self . cmd, e) ;
328+ match thread. join ( ) {
329+ Err ( e) => {
330+ return Err ( Error :: new (
331+ ErrorKind :: Other ,
332+ format ! (
333+ "Running [{}] stderr thread joined with error: {e:?}" ,
334+ self . cmd
335+ ) ,
336+ ) )
337+ }
338+ Ok ( output) => return Ok ( output) ,
307339 }
308340 }
341+ Ok ( "" . into ( ) )
342+ }
343+ }
344+
345+ impl Drop for StderrThread {
346+ fn drop ( & mut self ) {
347+ let _ = self . join ( ) ;
309348 }
310349}
0 commit comments