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 show: Show,
32}
33
34#[must_use]
60pub fn run(args: RunArgs) -> ExitCode {
61 let (send, receive) = mpsc::channel();
62
63 for call in args.executables {
65 let send_clone = send.clone();
66 thread::spawn(move || {
67 let _ = send_clone.send(subshell::run(call));
68 });
69 }
70
71 drop(send);
73
74 let mut exit_code = 0;
76 for call_result in receive {
77 match call_result {
78 Ok(call_result) => {
79 exit_code = exit_code.max(call_result.exit_code());
80 let error_from_output = args.error_on_output && call_result.has_output();
81 if error_from_output {
82 exit_code = exit_code.max(1);
83 }
84 let call_failed = !call_result.success() || error_from_output;
85 print_result(&call_result, call_failed, args.show);
86 }
87 Err(err) => {
88 eprintln!("{}", err.to_string().red());
89 exit_code = exit_code.max(1);
90 }
91 }
92 }
93 ExitCode::from(exit_code)
94}
95
96fn print_result(call_result: &CallResult, is_failed: bool, show: Show) {
98 let mut stdout = io::stdout();
99 let mut stderr = io::stderr();
100
101 if show.display_command() {
103 let mut command = call_result.name.clone();
104 if is_failed {
105 let _ = writeln!(stdout, "{}", command.bold().red());
106 } else {
107 if show.display_success() {
108 command = command.bold().to_string();
109 }
110 let _ = writeln!(stdout, "{command}");
111 }
112 }
113
114 if is_failed || show.display_success() {
116 write_output(&mut stdout, &call_result.output.stdout);
117 write_output(&mut stderr, &call_result.output.stderr);
118 }
119}
120
121fn write_output(writer: &mut dyn Write, output: &[u8]) {
122 if !output.is_empty() {
123 let _ = writer.write_all(output);
124 if !output.ends_with(b"\n") {
125 let _ = writer.write_all(b"\n");
126 }
127 }
128}