Skip to main content

nil_util/
process.rs

1// Copyright (C) Call of Nil contributors
2// SPDX-License-Identifier: AGPL-3.0-only
3
4use 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}