1use super::CallResult;
2use super::Show;
3use crate::library::subshell;
4use colored::Colorize;
5use std::io::{self, Write};
6use std::process::Command;
7use std::process::ExitCode;
8use std::sync::mpsc;
9use std::thread;
10
11#[derive(Debug)]
13pub struct Executable {
14 pub name: String,
16
17 pub command: Command,
19}
20
21#[derive(Debug)]
23pub struct RunArgs {
24 pub executables: Vec<Executable>,
26
27 pub error_on_output: bool,
29
30 pub stderr_to_stdout: bool,
32
33 pub show: Show,
35}
36
37#[must_use]
64pub fn run(args: RunArgs) -> ExitCode {
65 let (send, receive) = mpsc::channel();
66
67 for call in args.executables {
69 let send_clone = send.clone();
70 thread::spawn(move || {
71 let _ = send_clone.send(subshell::run(call));
72 });
73 }
74
75 drop(send);
77
78 let mut exit_code = 0;
80 for call_result in receive {
81 match call_result {
82 Ok(call_result) => {
83 exit_code = exit_code.max(call_result.exit_code());
84 let error_from_output = args.error_on_output && call_result.has_output();
85 if error_from_output {
86 exit_code = exit_code.max(1);
87 }
88 let call_failed = !call_result.success() || error_from_output;
89 print_result(&call_result, call_failed, args.show, args.stderr_to_stdout);
90 }
91 Err(err) => {
92 println!("{}", err.to_string().red());
93 exit_code = exit_code.max(1);
94 }
95 }
96 }
97 ExitCode::from(exit_code)
98}
99
100fn print_result(call_result: &CallResult, is_failed: bool, show: Show, stderr_to_stdout: bool) {
102 let mut stdout = io::stdout();
103 let mut stderr = io::stderr();
104
105 if show.display_command() {
107 let mut command = call_result.name.clone();
108 if is_failed {
109 let _ = writeln!(stdout, "{}", command.bold().red());
110 } else {
111 if show.display_success() {
112 command = command.bold().to_string();
113 }
114 let _ = writeln!(stdout, "{command}");
115 }
116 }
117
118 if is_failed || show.display_success() {
120 write_output(&mut stdout, &call_result.output.stdout);
121 if stderr_to_stdout {
122 write_output(&mut stdout, &call_result.output.stderr);
123 } else {
124 write_output(&mut stderr, &call_result.output.stderr);
125 }
126 }
127}
128
129fn write_output(writer: &mut dyn Write, output: &[u8]) {
130 if !output.is_empty() {
131 let _ = writer.write_all(output);
132 if !output.ends_with(b"\n") {
133 let _ = writer.write_all(b"\n");
134 }
135 }
136}