use duct::cmd as duct_cmd; use std::collections::HashMap; use std::path::PathBuf; #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct Cmd { program: String, #[serde(default)] args: Vec, #[serde(default)] stdin: Option, #[serde(default)] chdir: Option, #[serde(default)] env: HashMap, } impl Cmd { pub fn new>(program: T) -> Cmd { Cmd { program: program.as_ref().to_string(), args: Vec::new(), stdin: None, chdir: None, env: HashMap::new(), } } pub fn arg>(self, arg: T) -> Cmd { let Self { program, mut args, stdin, chdir, env, .. } = self; args.push(arg.as_ref().to_string()); Cmd { program, args, stdin, chdir, env, } } pub fn args>(self, new_args: & [ T ]) -> Cmd { let Self { program, mut args, stdin, chdir, env, .. } = self; for arg in new_args { args.push(arg.as_ref().to_string()); } Cmd { program, args, stdin, chdir, env, } } pub fn stdin>(self, input: T) -> Cmd { let Self { program, args, mut stdin, chdir, env, .. } = self; 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, env, } } pub fn chdir>(self, dir: T) -> Cmd { let Self { program, args, stdin, env, .. } = self; Cmd { program, args, stdin, chdir: Some(PathBuf::from(dir.as_ref())), env, } } pub fn env, V: AsRef>(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, } } pub fn run(self) -> Result { let Self { program, args, stdin, chdir, env, .. } = self; let mut cmd = duct_cmd(&program, &args); if ! env.is_empty() { for (key, value) in env { cmd = cmd.env(key, value); } } 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, pub success: bool, pub stdin: Option, pub dir: Option, pub stdout: String, pub stderr: String, }