1use std::ffi::{OsStr, OsString};
4use std::io;
5use std::os::windows::io::{AsHandle, OwnedHandle};
6use std::path::{Path, PathBuf};
7use std::process::{ExitStatus, Output};
8
9use crate::child::{Child, SuspendedChild};
10use crate::handles::Stdio;
11use crate::options::SpawnOptions;
12use crate::plan::{IoMode, SpawnPlan};
13use crate::sys;
14use crate::transaction::SpawnTransaction;
15
16#[derive(Debug)]
17pub(crate) enum Arg {
18 Text(OsString),
19 Raw(OsString),
20 Handle(OwnedHandle),
21}
22
23#[derive(Debug)]
24pub(crate) enum EnvOp {
25 Set(OsString, EnvValue),
26 Remove(OsString),
27}
28
29#[derive(Debug)]
30pub(crate) enum EnvValue {
31 Text(OsString),
32 Handle(OwnedHandle),
33}
34
35#[derive(Debug)]
62pub struct Command {
63 pub(crate) program: OsString,
64 pub(crate) args: Vec<Arg>,
65 pub(crate) env_clear: bool,
66 pub(crate) env_ops: Vec<EnvOp>,
67 pub(crate) cwd: Option<PathBuf>,
68 pub(crate) stdin: Option<Stdio>,
69 pub(crate) stdout: Option<Stdio>,
70 pub(crate) stderr: Option<Stdio>,
71}
72
73impl Command {
74 #[must_use]
76 pub fn new<S: AsRef<OsStr>>(program: S) -> Self {
77 Self {
78 program: program.as_ref().to_os_string(),
79 args: Vec::new(),
80 env_clear: false,
81 env_ops: Vec::new(),
82 cwd: None,
83 stdin: None,
84 stdout: None,
85 stderr: None,
86 }
87 }
88
89 pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Self {
91 self.args.push(Arg::Text(arg.as_ref().to_os_string()));
92 self
93 }
94
95 pub fn args<I, S>(&mut self, args: I) -> &mut Self
97 where
98 I: IntoIterator<Item = S>,
99 S: AsRef<OsStr>,
100 {
101 for arg in args {
102 self.arg(arg);
103 }
104 self
105 }
106
107 pub fn raw_arg<S: AsRef<OsStr>>(&mut self, text: S) -> &mut Self {
112 self.args.push(Arg::Raw(text.as_ref().to_os_string()));
113 self
114 }
115
116 pub fn arg_handle<T: AsHandle>(&mut self, handle: &T) -> io::Result<&mut Self> {
122 self.args.push(Arg::Handle(sys::duplicate_local(
123 handle.as_handle(),
124 false,
125 )?));
126 Ok(self)
127 }
128
129 pub fn env<K, V>(&mut self, key: K, value: V) -> &mut Self
131 where
132 K: AsRef<OsStr>,
133 V: AsRef<OsStr>,
134 {
135 self.env_ops.push(EnvOp::Set(
136 key.as_ref().to_os_string(),
137 EnvValue::Text(value.as_ref().to_os_string()),
138 ));
139 self
140 }
141
142 pub fn envs<I, K, V>(&mut self, vars: I) -> &mut Self
144 where
145 I: IntoIterator<Item = (K, V)>,
146 K: AsRef<OsStr>,
147 V: AsRef<OsStr>,
148 {
149 for (key, value) in vars {
150 self.env(key, value);
151 }
152 self
153 }
154
155 pub fn env_handle<K: AsRef<OsStr>, T: AsHandle>(
161 &mut self,
162 key: K,
163 handle: &T,
164 ) -> io::Result<&mut Self> {
165 self.env_ops.push(EnvOp::Set(
166 key.as_ref().to_os_string(),
167 EnvValue::Handle(sys::duplicate_local(handle.as_handle(), false)?),
168 ));
169 Ok(self)
170 }
171
172 pub fn env_remove<K: AsRef<OsStr>>(&mut self, key: K) -> &mut Self {
174 self.env_ops
175 .push(EnvOp::Remove(key.as_ref().to_os_string()));
176 self
177 }
178
179 pub fn env_clear(&mut self) -> &mut Self {
181 self.env_clear = true;
182 self.env_ops.clear();
183 self
184 }
185
186 pub fn current_dir<P: AsRef<Path>>(&mut self, dir: P) -> &mut Self {
188 self.cwd = Some(dir.as_ref().to_path_buf());
189 self
190 }
191
192 pub fn stdin<T: Into<Stdio>>(&mut self, stdio: T) -> &mut Self {
194 self.stdin = Some(stdio.into());
195 self
196 }
197
198 pub fn stdout<T: Into<Stdio>>(&mut self, stdio: T) -> &mut Self {
200 self.stdout = Some(stdio.into());
201 self
202 }
203
204 pub fn stderr<T: Into<Stdio>>(&mut self, stdio: T) -> &mut Self {
206 self.stderr = Some(stdio.into());
207 self
208 }
209
210 #[must_use]
212 pub fn get_program(&self) -> &OsStr {
213 &self.program
214 }
215
216 #[must_use]
218 pub fn get_current_dir(&self) -> Option<&Path> {
219 self.cwd.as_deref()
220 }
221
222 pub fn spawn(&mut self) -> io::Result<Child> {
228 self.spawn_with(SpawnOptions::new())
229 }
230
231 pub fn spawn_with(&mut self, options: SpawnOptions<'_>) -> io::Result<Child> {
237 let plan = SpawnPlan::new_running(self, options, IoMode::Spawn)?;
238 Ok(SpawnTransaction::new(&plan)?.commit_child())
239 }
240
241 pub fn spawn_suspended(&mut self) -> io::Result<SuspendedChild> {
247 self.spawn_suspended_with(SpawnOptions::new())
248 }
249
250 pub fn spawn_suspended_with(
256 &mut self,
257 options: SpawnOptions<'_>,
258 ) -> io::Result<SuspendedChild> {
259 let plan = SpawnPlan::new_suspended(self, options, IoMode::Spawn)?;
260 Ok(SpawnTransaction::new(&plan)?.commit_suspended())
261 }
262
263 pub fn status(&mut self) -> io::Result<ExitStatus> {
269 self.status_with(SpawnOptions::new())
270 }
271
272 pub fn status_with(&mut self, options: SpawnOptions<'_>) -> io::Result<ExitStatus> {
278 self.spawn_with(options)?.wait()
279 }
280
281 pub fn output(&mut self) -> io::Result<Output> {
287 self.output_with(SpawnOptions::new())
288 }
289
290 pub fn output_with(&mut self, options: SpawnOptions<'_>) -> io::Result<Output> {
296 let plan = SpawnPlan::new_running(self, options, IoMode::Output)?;
297 SpawnTransaction::new(&plan)?
298 .commit_child()
299 .wait_with_output()
300 }
301}