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