1use crate::library::subshell;
2
3use super::CallResult;
4use super::Show;
5use colored::Colorize;
6use std::io::{self, Write};
7use std::process::ExitCode;
8use std::sync::mpsc;
9use std::thread;
10
11#[derive(Debug, Eq, PartialEq)]
13pub struct RunArgs {
14 pub commands: Vec<String>,
16
17 pub error_on_output: bool,
19
20 pub show: Show,
22}
23
24#[must_use]
42pub fn run(args: RunArgs) -> ExitCode {
43 let (send, receive) = mpsc::channel();
44
45 for call in args.commands {
47 let send_clone = send.clone();
48 thread::spawn(move || {
49 let _ = send_clone.send(subshell::run(call));
50 });
51 }
52
53 drop(send);
55
56 let mut exit_code = 0;
58 for call_result in receive {
59 match call_result {
60 Ok(call_result) => {
61 exit_code = exit_code.max(call_result.exit_code());
62 let error_from_output = args.error_on_output && call_result.has_output();
63 if error_from_output {
64 exit_code = exit_code.max(1);
65 }
66 let call_failed = !call_result.success() || error_from_output;
67 print_result(&call_result, call_failed, args.show);
68 }
69 Err(err) => {
70 eprintln!("{}", err.to_string().red());
71 exit_code = exit_code.max(1);
72 }
73 }
74 }
75 ExitCode::from(exit_code)
76}
77
78fn print_result(call_result: &CallResult, is_failed: bool, show: Show) {
80 let mut stdout = io::stdout();
81 let mut stderr = io::stderr();
82
83 if show.display_command() {
85 let mut command = call_result.command.clone();
86 if is_failed {
87 let _ = writeln!(stdout, "{}", command.bold().red());
88 } else {
89 if show.display_success() {
90 command = command.bold().to_string();
91 }
92 let _ = writeln!(stdout, "{command}");
93 }
94 }
95
96 if is_failed || show.display_success() {
98 write_output(&mut stdout, &call_result.output.stdout);
99 write_output(&mut stderr, &call_result.output.stderr);
100 }
101}
102
103fn write_output(writer: &mut dyn Write, output: &[u8]) {
104 if !output.is_empty() {
105 let _ = writer.write_all(output);
106 if !output.ends_with(b"\n") {
107 let _ = writer.write_all(b"\n");
108 }
109 }
110}