greentic_dev/util/
process.rs

1use std::ffi::OsString;
2use std::path::PathBuf;
3use std::process::{Command, ExitStatus, Stdio};
4
5use anyhow::{Context, Result};
6
7#[derive(Clone, Copy, Debug, Eq, PartialEq)]
8pub enum StreamMode {
9    Inherit,
10    Capture,
11}
12
13pub struct CommandSpec {
14    pub program: OsString,
15    pub args: Vec<OsString>,
16    pub env: Vec<(OsString, OsString)>,
17    pub current_dir: Option<PathBuf>,
18    pub stdout: StreamMode,
19    pub stderr: StreamMode,
20}
21
22impl CommandSpec {
23    pub fn new(program: impl Into<OsString>) -> Self {
24        Self {
25            program: program.into(),
26            args: Vec::new(),
27            env: Vec::new(),
28            current_dir: None,
29            stdout: StreamMode::Inherit,
30            stderr: StreamMode::Inherit,
31        }
32    }
33}
34
35pub struct CommandOutput {
36    pub status: ExitStatus,
37    #[allow(dead_code)]
38    pub stdout: Option<Vec<u8>>,
39    pub stderr: Option<Vec<u8>>,
40}
41
42pub fn run(spec: CommandSpec) -> Result<CommandOutput> {
43    let mut command = Command::new(&spec.program);
44    command.args(&spec.args);
45    if let Some(dir) = &spec.current_dir {
46        command.current_dir(dir);
47    }
48    for (key, value) in &spec.env {
49        command.env(key, value);
50    }
51
52    match (spec.stdout, spec.stderr) {
53        (StreamMode::Inherit, StreamMode::Inherit) => {
54            command.stdout(Stdio::inherit());
55            command.stderr(Stdio::inherit());
56            let status = command
57                .status()
58                .with_context(|| format!("failed to spawn `{}`", spec.program.to_string_lossy()))?;
59            Ok(CommandOutput {
60                status,
61                stdout: None,
62                stderr: None,
63            })
64        }
65        (StreamMode::Capture, StreamMode::Capture) => {
66            command.stdout(Stdio::piped());
67            command.stderr(Stdio::piped());
68            let output = command
69                .output()
70                .with_context(|| format!("failed to spawn `{}`", spec.program.to_string_lossy()))?;
71            Ok(CommandOutput {
72                status: output.status,
73                stdout: Some(output.stdout),
74                stderr: Some(output.stderr),
75            })
76        }
77        _ => anyhow::bail!("mixed capture/inherit mode is not supported yet"),
78    }
79}