1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
// This is free and unencumbered software released into the public domain.
use clientele::SysexitsError::{self, *};
use std::process::{ExitStatus, Stdio};
use crate::{Result, shared::locate_subcommand};
pub struct ExternalResult {
/// Return code of the executed command.
pub code: SysexitsError,
/// If `pipe_output` is `true`, this field contains stdout, otherwise its None.
pub stdout: Option<Vec<u8>>,
/// If `pipe_output` is `true`, this field contains stderr, otherwise its None.
pub stderr: Option<Vec<u8>>,
}
/// Executes the given subcommand.
pub struct External {
pub is_debug: bool,
pub pipe_output: bool,
}
impl External {
pub fn execute(&self, cmd: &str, args: impl AsRef<[String]>) -> Result<ExternalResult> {
// Locate the given subcommand:
let cmd = locate_subcommand(cmd)?;
// Prepare the process:
let result: std::io::Result<(ExitStatus, Option<Vec<u8>>, Option<Vec<u8>>)> =
if self.pipe_output {
std::process::Command::new(&cmd.path)
.args(args.as_ref())
.stdin(Stdio::inherit())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.output()
.map(|x| (x.status, Some(x.stdout), Some(x.stderr)))
} else {
std::process::Command::new(&cmd.path)
.args(args.as_ref())
.stdin(Stdio::inherit())
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.status()
.map(|x| (x, None, None))
};
match result {
Err(error) => {
if self.is_debug {
eprintln!("asimov: {}", error);
}
Err(EX_SOFTWARE)
},
Ok(result) => {
#[cfg(unix)]
{
use std::os::unix::process::ExitStatusExt;
if let Some(signal) = result.0.signal() {
if self.is_debug {
eprintln!("asimov: terminated by signal {}", signal);
}
return Ok(ExternalResult {
code: SysexitsError::try_from((signal | 0x80) & 0xff)
.unwrap_or(EX_SOFTWARE),
stdout: result.1,
stderr: result.2,
});
}
}
Ok(ExternalResult {
// unwrap_or should never happen because we are handling signal above.
code: result
.0
.code()
.and_then(|code| SysexitsError::try_from(code).ok())
.unwrap_or(EX_SOFTWARE),
stdout: result.1,
stderr: result.2,
})
},
}
}
}