2 ffi::{OsStr, OsString},
5 process::{Command, Output},
8 use format::lazy_format;
9 use miette::{ensure, Context, IntoDiagnostic};
11 pub(crate) fn which(name: &'static str, desc: &str) -> miette::Result<OsString> {
12 let found = ::which::which(name)
14 .wrap_err(lazy_format!("failed to find `{name}` executable"))?;
15 log::debug!("using {desc} from {}", found.display());
16 Ok(found.file_name().unwrap().to_owned())
19 pub(crate) struct EasyCommand {
24 pub(crate) fn new<C>(cmd: C, f: impl FnOnce(&mut Command) -> &mut Command) -> Self
28 let mut cmd = Command::new(cmd);
33 pub(crate) fn spawn(&mut self) -> miette::Result<()> {
34 log::debug!("spawning {self}…");
39 .wrap_err_with(|| format!("failed to spawn {self}"))?
42 .wrap_err_with(|| format!("failed to wait for exit code from {self}"))?;
43 log::debug!("{self} returned {:?}", status.code());
44 ensure!(status.success(), "{self} returned {:?}", status.code());
48 fn just_stdout(&mut self) -> miette::Result<Vec<u8>> {
49 log::debug!("getting `stdout` output of {self}");
54 .wrap_err_with(|| format!("failed to execute `{self}`"))?;
60 log::debug!("{self} returned {:?}", status.code());
63 "{self} returned {:?}; full output: {output:#?}",
66 assert!(stderr.is_empty());
70 pub(crate) fn just_stdout_utf8(&mut self) -> miette::Result<String> {
71 String::from_utf8(self.just_stdout()?)
73 .wrap_err_with(|| format!("output of {self} was not UTF-8 (!?)"))
77 impl Display for EasyCommand {
78 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79 let Self { inner } = self;
80 let prog = inner.get_program().to_string_lossy();
81 let args = inner.get_args().map(|a| a.to_string_lossy());
82 let shell_words = ::shell_words::join(once(prog).chain(args));
83 write!(f, "`{shell_words}`")