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