1use crate::library::run::Executable;
2use std::io;
3use std::process::Command;
4
5pub fn run(Executable { mut command, name }: Executable) -> Result<CallResult, RunError> {
7 match command.output() {
8 Ok(output) => Ok(CallResult { name, output }),
9 Err(error) => Err(RunError { name, error }),
10 }
11}
12
13pub struct CallResult {
15 pub name: String,
16 pub output: std::process::Output,
17}
18
19impl CallResult {
20 pub(crate) fn exit_code(&self) -> u8 {
21 if self.output.status.success() {
22 0
23 } else {
24 to_exitcode_u8(self.output.status.code().unwrap_or(1))
25 }
26 }
27
28 pub(crate) fn has_output(&self) -> bool {
30 !self.output.stdout.is_empty() || !self.output.stderr.is_empty()
31 }
32
33 pub(crate) fn success(&self) -> bool {
35 self.output.status.success()
36 }
37}
38
39#[must_use]
45pub fn shell_executable<IS: Into<String>>(command: IS) -> Executable {
46 let name = command.into();
47 let command = shell_command(&name);
48 Executable { name, command }
49}
50
51#[cfg(unix)]
57#[must_use]
58pub fn shell_command(command: &str) -> Command {
59 let mut cmd = Command::new("sh");
60 cmd.arg("-c").arg(command);
61 cmd
62}
63
64#[cfg(windows)]
70#[must_use]
71pub fn shell_command(command: &str) -> Command {
72 let mut cmd = Command::new("cmd.exe");
73 cmd.arg("/C").arg(command);
74 cmd
75}
76
77fn to_exitcode_u8(value: i32) -> u8 {
78 if value == i32::MIN {
79 return 255;
80 }
81 u8::try_from(value.abs()).unwrap_or(255)
82}
83
84pub struct RunError {
85 pub name: String,
87 pub error: io::Error,
89}
90
91impl std::fmt::Display for RunError {
92 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93 write!(f, "Cannot execute '{}': {}", self.name, self.error)
94 }
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100
101 #[test]
102 fn test_safe_convert_to_u8() {
103 assert_eq!(to_exitcode_u8(0), 0);
104 assert_eq!(to_exitcode_u8(1), 1);
105 assert_eq!(to_exitcode_u8(-1), 1);
106 assert_eq!(to_exitcode_u8(255), 255);
107 assert_eq!(to_exitcode_u8(-255), 255);
108 assert_eq!(to_exitcode_u8(256), 255);
109 assert_eq!(to_exitcode_u8(-256), 255);
110 assert_eq!(to_exitcode_u8(i32::MAX), 255);
111 assert_eq!(to_exitcode_u8(i32::MIN), 255);
112 }
113}