use duct::cmd as duct_cmd; use std::path::PathBuf; #[derive(Clone, Debug, Serialize)] pub struct Cmd { program: String, args: Vec, stdin: Option, chdir: Option, } impl Cmd { pub fn new>(program: T) -> Cmd { Cmd { program: program.as_ref().to_string(), args: Vec::new(), stdin: None, chdir: None, } } pub fn arg>(self, arg: T) -> Cmd { let Self { program, mut args, stdin, chdir, .. } = self; args.push(arg.as_ref().to_string()); Cmd { program, args, stdin, chdir, } } pub fn args>(self, new_args: & [ T ]) -> Cmd { let Self { program, mut args, stdin, chdir, .. } = self; for arg in new_args { args.push(arg.as_ref().to_string()); } Cmd { program, args, stdin, chdir, } } pub fn stdin>(self, input: T) -> Cmd { let Self { program, args, mut stdin, chdir, .. } = 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, } } pub fn chdir>(self, dir: T) -> Cmd { let Self { program, args, stdin, .. } = self; Cmd { program, args, stdin, chdir: Some(PathBuf::from(dir.as_ref())), } } pub fn run(self) -> Result { let Self { program, args, stdin, chdir, .. } = self; let mut cmd = duct_cmd(&program, &args); 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, }