1use std::collections::BTreeMap;
2use std::ffi::OsStr;
3use std::path::Path;
4use std::process::Command;
5
6use crate::error::CargoResult;
7
8fn do_call(
9 command: impl IntoIterator<Item = impl Into<String>>,
10 path: Option<&Path>,
11 envs: Option<BTreeMap<&OsStr, &OsStr>>,
12 dry_run: bool,
13) -> CargoResult<bool> {
14 let command: Vec<_> = command.into_iter().map(|s| s.into()).collect();
15 if path.is_some() {
16 log::trace!("cd {}", path.unwrap().display());
17 }
18 log::trace!("{}", command.join(" "));
19 if dry_run {
20 return Ok(true);
21 }
22 let mut iter = command.iter();
23 let cmd_name = iter.next().unwrap();
24
25 let mut cmd = Command::new(cmd_name);
26
27 if let Some(p) = path {
28 cmd.current_dir(p);
29 }
30
31 if let Some(e) = envs {
32 cmd.envs(e.iter());
33 }
34
35 for arg in iter {
36 if !arg.is_empty() {
37 cmd.arg(arg);
38 }
39 }
40
41 let ctx_dir = match path {
42 Some(p) => format!(" within {}", p.display()),
43 None => String::new(),
44 };
45
46 let mut child = cmd
47 .spawn()
48 .map_err(|e| anyhow::format_err!("failed to launch `{cmd_name}`{ctx_dir}: {e}"))?;
49 let result = child
50 .wait()
51 .map_err(|e| anyhow::format_err!("failed to launch `{cmd_name}`{ctx_dir}: {e}"))?;
52
53 Ok(result.success())
54}
55
56pub fn call(
57 command: impl IntoIterator<Item = impl Into<String>>,
58 dry_run: bool,
59) -> CargoResult<bool> {
60 do_call(command, None, None, dry_run)
61}
62
63pub fn call_on_path(
64 command: impl IntoIterator<Item = impl Into<String>>,
65 path: &Path,
66 dry_run: bool,
67) -> CargoResult<bool> {
68 do_call(command, Some(path), None, dry_run)
69}
70
71pub fn call_with_env(
72 command: impl IntoIterator<Item = impl Into<String>>,
73 envs: BTreeMap<&OsStr, &OsStr>,
74 path: &Path,
75 dry_run: bool,
76) -> CargoResult<bool> {
77 do_call(command, Some(path), Some(envs), dry_run)
78}