1use anyhow::Result;
5use std::ffi::OsStr;
6use std::process::Command;
7
8pub fn command<Args, Arg, Env, Key, Value>(program: &str, args: Args, env: Option<Env>) -> Command
9where
10 Args: IntoIterator<Item = Arg>,
11 Arg: AsRef<OsStr>,
12 Env: IntoIterator<Item = (Key, Value)>,
13 Key: AsRef<OsStr>,
14 Value: AsRef<OsStr>,
15{
16 let mut command = if cfg!(windows) { Command::new("pwsh") } else { Command::new(program) };
17
18 if cfg!(windows) {
19 command
20 .args(["-Command", program])
21 .args(args);
22 } else {
23 command.args(args);
24 }
25
26 if let Some(envs) = env {
27 command.envs(envs);
28 }
29
30 command
31}
32
33pub fn output<Args, Arg, Env, Key, Value>(
34 program: &str,
35 args: Args,
36 env: Option<Env>,
37) -> Result<Vec<u8>>
38where
39 Args: IntoIterator<Item = Arg>,
40 Arg: AsRef<OsStr>,
41 Env: IntoIterator<Item = (Key, Value)>,
42 Key: AsRef<OsStr>,
43 Value: AsRef<OsStr>,
44{
45 command(program, args, env)
46 .output()?
47 .exit_ok()
48 .map(|it| it.stdout)
49 .map_err(Into::into)
50}
51
52pub fn spawn<Args, Arg, Env, Key, Value>(program: &str, args: Args, env: Option<Env>) -> Result<()>
53where
54 Args: IntoIterator<Item = Arg>,
55 Arg: AsRef<OsStr>,
56 Env: IntoIterator<Item = (Key, Value)>,
57 Key: AsRef<OsStr>,
58 Value: AsRef<OsStr>,
59{
60 command(program, args, env)
61 .status()?
62 .exit_ok()
63 .map_err(Into::into)
64}
65
66#[doc(hidden)]
67#[macro_export]
68macro_rules! into_command_parts {
69 ($command:expr) => {{
70 let env: [(&str, &str); 0] = [];
71 $crate::command!($command, env)
72 }};
73 ($command:expr, $env:expr) => {{
74 let mut iter = $command
75 .trim()
76 .split_whitespace()
77 .map(|it| it.trim())
78 .filter(|it| !it.is_empty());
79
80 let Some(program) = iter.next() else {
81 anyhow::bail!("Missing program");
82 };
83
84 (program, iter)
85 }};
86}
87
88#[macro_export]
89macro_rules! output {
90 ($command:expr) => {{
91 let env: [(&str, &str); 0] = [];
92 $crate::output!($command, env)
93 }};
94 ($command:expr, $env:expr) => {{
95 let (program, args) = $crate::into_command_parts!($command, $env);
96 $crate::process::output(program, args, Some($env))
97 }};
98}
99
100#[macro_export]
101macro_rules! output_fmt {
102 ($($arg:tt)*) => {{
103 let command = format!($($arg)*);
104 $crate::output!(command)
105 }};
106}
107
108#[macro_export]
109macro_rules! spawn {
110 ($command:expr) => {{
111 let env: [(&str, &str); 0] = [];
112 $crate::spawn!($command, env)
113 }};
114 ($command:expr, $env:expr) => {{
115 let (program, args) = $crate::into_command_parts!($command, $env);
116 $crate::process::spawn(program, args, Some($env))
117 }};
118}
119
120#[macro_export]
121macro_rules! spawn_fmt {
122 ($($arg:tt)*) => {{
123 let command = format!($($arg)*);
124 $crate::spawn!(command)
125 }};
126}