2023-04-19 21:49:30 +02:00
|
|
|
use duct::cmd as duct_cmd;
|
|
|
|
|
2023-04-20 18:31:45 +02:00
|
|
|
use std::collections::HashMap;
|
2023-04-19 21:49:30 +02:00
|
|
|
use std::path::PathBuf;
|
|
|
|
|
2023-10-05 17:58:41 +02:00
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
2023-04-19 21:49:30 +02:00
|
|
|
pub struct Cmd {
|
2023-10-05 17:58:41 +02:00
|
|
|
program: String,
|
|
|
|
#[serde(default)]
|
|
|
|
args: Vec<String>,
|
|
|
|
#[serde(default)]
|
|
|
|
stdin: Option<String>,
|
|
|
|
#[serde(default)]
|
|
|
|
chdir: Option<PathBuf>,
|
|
|
|
#[serde(default)]
|
|
|
|
env: HashMap<String, String>,
|
2023-04-19 21:49:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Cmd {
|
|
|
|
pub fn new<T: AsRef<str>>(program: T) -> Cmd {
|
|
|
|
Cmd {
|
|
|
|
program: program.as_ref().to_string(),
|
|
|
|
args: Vec::new(),
|
|
|
|
stdin: None,
|
|
|
|
chdir: None,
|
2023-04-20 18:31:45 +02:00
|
|
|
env: HashMap::new(),
|
2023-04-19 21:49:30 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn arg<T: AsRef<str>>(self, arg: T) -> Cmd {
|
2023-04-20 18:31:45 +02:00
|
|
|
let Self { program, mut args, stdin, chdir, env, .. } = self;
|
2023-04-19 21:49:30 +02:00
|
|
|
|
|
|
|
args.push(arg.as_ref().to_string());
|
|
|
|
|
|
|
|
Cmd {
|
|
|
|
program,
|
|
|
|
args,
|
|
|
|
stdin,
|
|
|
|
chdir,
|
2023-04-20 18:31:45 +02:00
|
|
|
env,
|
2023-04-19 21:49:30 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn args<T: AsRef<str>>(self, new_args: & [ T ]) -> Cmd {
|
|
|
|
|
2023-04-20 18:31:45 +02:00
|
|
|
let Self { program, mut args, stdin, chdir, env, .. } = self;
|
2023-04-19 21:49:30 +02:00
|
|
|
|
|
|
|
for arg in new_args {
|
|
|
|
args.push(arg.as_ref().to_string());
|
|
|
|
}
|
|
|
|
|
|
|
|
Cmd {
|
|
|
|
program,
|
|
|
|
args,
|
|
|
|
stdin,
|
|
|
|
chdir,
|
2023-04-20 18:31:45 +02:00
|
|
|
env,
|
|
|
|
}
|
|
|
|
}
|
2023-04-19 21:49:30 +02:00
|
|
|
|
|
|
|
pub fn stdin<T: AsRef<str>>(self, input: T) -> Cmd {
|
2023-04-20 18:31:45 +02:00
|
|
|
let Self { program, args, mut stdin, chdir, env, .. } = self;
|
2023-04-19 21:49:30 +02:00
|
|
|
|
|
|
|
stdin = if let Some(mut s) = stdin {
|
|
|
|
s.push_str(input.as_ref());
|
|
|
|
Some(s)
|
|
|
|
} else {
|
|
|
|
Some(input.as_ref().to_string())
|
|
|
|
};
|
|
|
|
|
|
|
|
Cmd {
|
|
|
|
program,
|
|
|
|
args,
|
|
|
|
stdin,
|
|
|
|
chdir,
|
2023-04-20 18:31:45 +02:00
|
|
|
env,
|
2023-04-19 21:49:30 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn chdir<T: AsRef<str>>(self, dir: T) -> Cmd {
|
2023-04-20 18:31:45 +02:00
|
|
|
let Self { program, args, stdin, env, .. } = self;
|
2023-04-19 21:49:30 +02:00
|
|
|
|
|
|
|
Cmd {
|
|
|
|
program,
|
|
|
|
args,
|
|
|
|
stdin,
|
|
|
|
chdir: Some(PathBuf::from(dir.as_ref())),
|
2023-04-20 18:31:45 +02:00
|
|
|
env,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn env<K: AsRef<str>, V: AsRef<str>>(self, key: K, value: V) -> Cmd {
|
|
|
|
let Self { program, args, stdin, chdir, mut env, .. } = self;
|
|
|
|
|
|
|
|
env.insert(key.as_ref().to_string(), value.as_ref().to_string());
|
|
|
|
|
|
|
|
Cmd {
|
|
|
|
program,
|
|
|
|
args,
|
|
|
|
stdin,
|
|
|
|
chdir,
|
|
|
|
env,
|
2023-04-19 21:49:30 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn run(self) -> Result<CmdOutput, std::io::Error> {
|
2023-04-20 18:31:45 +02:00
|
|
|
let Self { program, args, stdin, chdir, env, .. } = self;
|
2023-04-19 21:49:30 +02:00
|
|
|
|
|
|
|
let mut cmd = duct_cmd(&program, &args);
|
|
|
|
|
2023-04-20 18:31:45 +02:00
|
|
|
if ! env.is_empty() {
|
|
|
|
for (key, value) in env {
|
|
|
|
cmd = cmd.env(key, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-19 21:49:30 +02:00
|
|
|
if let Some(stdin) = &stdin {
|
|
|
|
cmd = cmd.stdin_bytes(stdin.as_bytes());
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(chdir) = &chdir {
|
|
|
|
cmd = cmd.dir(chdir);
|
|
|
|
}
|
|
|
|
|
|
|
|
let res = cmd.unchecked()
|
|
|
|
.stdout_capture()
|
|
|
|
.stderr_capture()
|
|
|
|
.run()?;
|
|
|
|
|
|
|
|
Ok(CmdOutput {
|
|
|
|
program,
|
|
|
|
args,
|
|
|
|
success: res.status.success(),
|
|
|
|
stdin,
|
|
|
|
dir: chdir,
|
|
|
|
stdout: String::from_utf8(res.stdout).unwrap(),
|
|
|
|
stderr: String::from_utf8(res.stderr).unwrap(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, Serialize)]
|
|
|
|
pub struct CmdOutput {
|
|
|
|
pub program: String,
|
|
|
|
pub args: Vec<String>,
|
|
|
|
pub success: bool,
|
|
|
|
pub stdin: Option<String>,
|
|
|
|
pub dir: Option<PathBuf>,
|
|
|
|
pub stdout: String,
|
|
|
|
pub stderr: String,
|
|
|
|
}
|