asimov_cli/commands/
external.rs1use clientele::SysexitsError::{self, *};
4use std::process::{ExitStatus, Stdio};
5
6use crate::shared::locate_subcommand;
7use crate::Result;
8
9pub struct ExternalResult {
10 pub code: SysexitsError,
12
13 pub stdout: Option<Vec<u8>>,
15
16 pub stderr: Option<Vec<u8>>,
18}
19
20pub struct External {
22 pub is_debug: bool,
23 pub pipe_output: bool,
24}
25
26impl External {
27 pub fn execute(&self, cmd: &str, args: &[String]) -> Result<ExternalResult> {
28 let cmd = locate_subcommand(cmd)?;
30
31 let result: std::io::Result<(ExitStatus, Option<Vec<u8>>, Option<Vec<u8>>)> =
33 if self.pipe_output {
34 std::process::Command::new(&cmd.path)
35 .args(args)
36 .stdin(Stdio::inherit())
37 .stdout(Stdio::piped())
38 .stderr(Stdio::piped())
39 .output()
40 .map(|x| (x.status, Some(x.stdout), Some(x.stderr)))
41 } else {
42 std::process::Command::new(&cmd.path)
43 .args(args)
44 .stdin(Stdio::inherit())
45 .stdout(Stdio::inherit())
46 .stderr(Stdio::inherit())
47 .status()
48 .map(|x| (x, None, None))
49 };
50
51 match result {
52 Err(error) => {
53 if self.is_debug {
54 eprintln!("{}: {}", "asimov", error);
55 }
56 Err(EX_SOFTWARE)
57 }
58 Ok(result) => {
59 #[cfg(unix)]
60 {
61 use std::os::unix::process::ExitStatusExt;
62
63 if let Some(signal) = result.0.signal() {
64 if self.is_debug {
65 eprintln!("{}: terminated by signal {}", "asimov", signal);
66 }
67
68 return Ok(ExternalResult {
69 code: SysexitsError::try_from((signal | 0x80) & 0xff)
70 .unwrap_or(EX_SOFTWARE),
71 stdout: result.1,
72 stderr: result.2,
73 });
74 }
75 }
76
77 Ok(ExternalResult {
78 code: result
80 .0
81 .code()
82 .and_then(|code| SysexitsError::try_from(code).ok())
83 .unwrap_or(EX_SOFTWARE),
84 stdout: result.1,
85 stderr: result.2,
86 })
87 }
88 }
89 }
90}