@@ -206,6 +206,16 @@ pub trait CommandBuilder {
206206
207207 /// Akin to [`Command::current_dir`].
208208 fn with_current_dir < P : AsRef < Path > > ( self , path : P ) -> Self ;
209+
210+ /// Pipe `stdout` of _this_ into `next` command.
211+ fn pipe ( self , next : Command ) -> Result < Self >
212+ where
213+ Self : Sized ;
214+
215+ /// Pipe `stderr` of _this_ into `next` command.
216+ fn pipe_stderr ( self , next : Command ) -> Result < Self >
217+ where
218+ Self : Sized ;
209219}
210220
211221impl CommandBuilder for Command {
@@ -227,6 +237,32 @@ impl CommandBuilder for Command {
227237 self . current_dir ( dir) ;
228238 self
229239 }
240+
241+ fn pipe ( mut self , mut next : Command ) -> Result < Self > {
242+ let cmd = self
243+ . stdout ( Stdio :: piped ( ) )
244+ . spawn ( )
245+ . map_err ( |e| miette ! ( "encountered error with command {}: {e}" , self . cmd_str( ) ) ) ?;
246+
247+ let out = cmd. stdout . expect ( "piped so should exist" ) ;
248+ let stdin = Stdio :: from ( out) ;
249+
250+ next. stdin ( stdin) ;
251+ Ok ( next)
252+ }
253+
254+ fn pipe_stderr ( mut self , mut next : Command ) -> Result < Self > {
255+ let cmd = self
256+ . stderr ( Stdio :: piped ( ) )
257+ . spawn ( )
258+ . map_err ( |e| miette ! ( "encountered error with command {}: {e}" , self . cmd_str( ) ) ) ?;
259+
260+ let out = cmd. stderr . expect ( "piped so should exist" ) ;
261+ let stdin = Stdio :: from ( out) ;
262+
263+ next. stdin ( stdin) ;
264+ Ok ( next)
265+ }
230266}
231267
232268/// Output [`Command`] as a text string, useful for debugging.
@@ -325,6 +361,7 @@ mod tests {
325361 let x = cmd ! ( watcmd: "foo" ) . execute_str ( Verbose ) . unwrap_err ( ) ;
326362 assert_snapshot ! ( "unknown-cmd" , pretty_print_err( x) ) ;
327363 }
364+
328365 #[ test]
329366 fn cmd_naming_with_env ( ) {
330367 let x = cmd ! ( ls) . with_env ( "YO" , "zog" ) . cmd_str ( ) ;
@@ -338,4 +375,39 @@ mod tests {
338375 . cmd_str ( ) ;
339376 assert_eq ! ( & x, "ls foo bar" ) ;
340377 }
378+
379+ #[ test]
380+ fn cmd_piping ( ) {
381+ let x = cmd ! ( ls)
382+ . pipe ( cmd ! ( grep: Cargo . * ) )
383+ . unwrap ( )
384+ . execute_str ( Quiet )
385+ . unwrap ( ) ;
386+ let mut x = x. trim ( ) . split ( '\n' ) . collect :: < Vec < _ > > ( ) ;
387+ x. sort ( ) ;
388+
389+ assert_eq ! ( & x, & [ "Cargo.lock" , "Cargo.toml" , ] ) ;
390+
391+ let x = cmd ! ( ls)
392+ . pipe ( cmd ! ( grep: Cargo . * ) )
393+ . unwrap ( )
394+ . pipe ( cmd ! ( grep: toml) )
395+ . unwrap ( )
396+ . execute_str ( Quiet )
397+ . unwrap ( ) ;
398+ let mut x = x. trim ( ) . split ( '\n' ) . collect :: < Vec < _ > > ( ) ;
399+ x. sort ( ) ;
400+
401+ assert_eq ! ( & x, & [ "Cargo.toml" , ] ) ;
402+
403+ let x = cmd ! ( ls: foo)
404+ . pipe_stderr ( cmd ! ( grep: foo) )
405+ . unwrap ( )
406+ . execute_str ( Quiet )
407+ . unwrap ( ) ;
408+ let mut x = x. trim ( ) . split ( '\n' ) . collect :: < Vec < _ > > ( ) ;
409+ x. sort ( ) ;
410+
411+ assert_eq ! ( & x, & [ "ls: cannot access 'foo': No such file or directory" , ] ) ;
412+ }
341413}
0 commit comments