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