1use std::io;
2use std::io::BufRead;
3use std::process::{Command, ExitStatus};
4use std::string::FromUtf8Error;
5
6#[derive(Debug)]
7pub enum CommandError {
8 Io(Box<io::Error>),
9 Stderr(String),
10 De(serde_json::Error),
11 Encoding(FromUtf8Error),
12 ExitStatus(ExitStatus),
13}
14
15impl From<io::Error> for CommandError {
16 fn from(e: io::Error) -> Self {
17 CommandError::Io(Box::new(e))
18 }
19}
20
21impl From<serde_json::Error> for CommandError {
22 fn from(e: serde_json::Error) -> Self {
23 CommandError::De(e)
24 }
25}
26
27pub fn run_cmd<F, T>(c: &mut Command, f: F) -> Result<T, CommandError>
28where
29 F: FnOnce(Vec<u8>) -> T,
30{
31 let output = c.output().map_err(Box::new).map_err(CommandError::Io)?;
32
33 if !output.status.success() {
34 return Err(CommandError::ExitStatus(output.status));
35 }
36
37 if !output.stderr.is_empty() {
38 return Err(CommandError::Stderr(
39 String::from_utf8(output.stderr).map_err(CommandError::Encoding)?,
40 ));
41 }
42
43 Ok(f(output.stdout))
44}
45
46pub fn run_cmd_stdout(c: &mut Command) -> Result<String, CommandError> {
47 run_cmd(c, String::from_utf8)?.map_err(CommandError::Encoding)
48}
49
50pub fn run_cmd_stdout_lines_capacity(
51 c: &mut Command,
52 lines_hint: usize,
53) -> Result<Vec<String>, CommandError> {
54 let mut ret = Vec::with_capacity(lines_hint);
55 ret.extend(run_cmd(c, |stdout| {
56 stdout
57 .lines()
58 .collect::<Result<_, io::Error>>()
59 .map_err(Into::<CommandError>::into)
60 })?);
61 Ok(ret)
62}
63
64pub fn run_cmd_stdout_lines(c: &mut Command) -> Result<Vec<String>, CommandError> {
65 run_cmd_stdout_lines_capacity(c, 64)
66}