1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
//! Composable wrappers over process::Command.
//!
//! # Quick start
//!
//! ```toml
//! [dependencies]
//! process-wrap = { version = "9.1.0", features = ["std"] }
//! ```
//!
//! ```rust,no_run
//! # fn main() -> std::io::Result<()> {
//! use process_wrap::std::*;
//!
//! let mut command = CommandWrap::with_new("watch", |command| { command.arg("ls"); });
//! #[cfg(unix)] { command.wrap(ProcessGroup::leader()); }
//! #[cfg(windows)] { command.wrap(JobObject); }
//! let mut child = command.spawn()?;
//! let status = child.wait()?;
//! dbg!(status);
//! # Ok(()) }
//! ```
//!
//! ## Migrating from command-group
//!
//! The above example is equivalent to the `command-group` 5.x usage. To migrate from versions 4.x
//! and below, replace `ProcessGroup::leader()` with `ProcessSession`.
//!
//! # Overview
//!
//! This crate provides a composable set of wrappers over `process::Command` (either from std or
//! from Tokio). It is a more flexible and composable successor to the `command-group` crate, and is
//! meant to be adaptable to additional use cases: for example spawning processes in PTYs currently
//! requires a different crate (such as `pty-process`) which won't function with `command-group`.
//! Implementing a PTY wrapper for `process-wrap` would instead keep the same API and be composable
//! with the existing process group/session implementations.
//!
//! # Usage
//!
//! The core API is [`CommandWrap`](std::CommandWrap) and [`CommandWrap`](tokio::CommandWrap),
//! which can be constructed either directly from an existing `process::Command`:
//!
//! ```rust
//! use process_wrap::std::*;
//! use std::process::Command;
//! let mut command = Command::new("ls");
//! command.arg("-l");
//! let mut command = CommandWrap::from(command);
//! #[cfg(unix)] { command.wrap(ProcessGroup::leader()); }
//! #[cfg(windows)] { command.wrap(JobObject); }
//! ```
//!
//! ...or with a somewhat more ergonomic closure pattern:
//!
//! ```rust
//! use process_wrap::std::*;
//! let mut command = CommandWrap::with_new("ls", |command| { command.arg("-l"); });
//! #[cfg(unix)] { command.wrap(ProcessGroup::leader()); }
//! #[cfg(windows)] { command.wrap(JobObject); }
//! ```
//!
//! If targetting a single platform, then a fluent style is possible:
//!
//! ```rust
//! use process_wrap::std::*;
//! CommandWrap::with_new("ls", |command| { command.arg("-l"); })
//! .wrap(ProcessGroup::leader());
//! ```
//!
//! The `wrap` method can be called multiple times to add multiple wrappers. The order of the
//! wrappers can be important, as they are applied in the order they are added. The documentation
//! for each wrapper will specify ordering concerns.
//!
//! The `spawn` method is used to spawn the process, after which the `Child` can be interacted with.
//! Methods on `Child` mimic those on `process::Child`, but may be customised by the wrappers. For
//! example, `kill` will send a signal to the process group if the `ProcessGroup` wrapper is used.
//!
//! # KillOnDrop and CreationFlags
//!
//! The options set on an underlying `Command` are not queryable from library or user code. In most
//! cases this is not an issue; however on Windows, the `JobObject` wrapper needs to know the value
//! of `.kill_on_drop()` and any `.creation_flags()` set. The `KillOnDrop` and `CreationFlags` are
//! "shims" that _should_ be used instead of the aforementioned methods on `Command`. They will
//! internally set the values on the `Command` and also store them in the wrapper, so that wrappers
//! are able to access them.
//!
//! In practice:
//!
//! ## Instead of `.kill_on_drop(true)` (Tokio-only):
//!
//! ```rust
//! use process_wrap::tokio::*;
//! let mut command = CommandWrap::with_new("ls", |command| { command.arg("-l"); });
//! command.wrap(KillOnDrop);
//! ```
//!
//! ## Instead of `.creation_flags(CREATE_NO_WINDOW)` (Windows-only):
//!
//! ```rust,ignore
//! use process_wrap::std::*;
//! let mut command = CommandWrap::with_new("ls", |command| { command.arg("-l"); });
//! command.wrap(CreationFlags(CREATE_NO_WINDOW));
//! ```
//!
//! Internally the `JobObject` wrapper always sets the `CREATE_SUSPENDED` flag, but as it is able to
//! access the `CreationFlags` value it will either resume the process after setting up, or leave it
//! suspended if `CREATE_SUSPENDED` was explicitly set.
//!
//! # Extension
//!
//! The crate is designed to be extensible, and new wrappers can be added by implementing the
//! required traits. The std and Tokio sides are completely separate, due to the different
//! underlying APIs. Of course you can (and should) re-use/share code wherever possible if
//! implementing both.
//!
//! At minimum, you must implement [`CommandWrapper`](crate::std::CommandWrapper) and/or
//! [`CommandWrapper`](crate::tokio::CommandWrapper). These provide the same functionality
//! (and indeed internally are generated using a common macro), but differ in the exact types used.
//! Here's the most basic impl (shown for Tokio):
//!
//! ```rust
//! use process_wrap::tokio::*;
//! #[derive(Debug)]
//! pub struct YourWrapper;
//! impl CommandWrapper for YourWrapper {}
//! ```
//!
//! The trait provides extension or hook points into the lifecycle of a `Command`:
//!
//! - **`fn extend(&mut self, other: Box<dyn CommandWrapper>)`** is called if
//! `.wrap(YourWrapper)` is done twice. Only one of a wrapper type can exist, so this gives the
//! opportunity to incorporate all or part of the second wrapper instance into the first. By
//! default, this does nothing (ie only the first registered wrapper instance of a type applies).
//!
//! - **`fn pre_spawn(&mut self, command: &mut Command, core: &CommandWrap)`** is called before
//! the command is spawned, and gives mutable access to it. It also gives mutable access to the
//! wrapper instance, so state can be stored if needed. The `core` reference gives access to data
//! from other wrappers; for example, that's how `CreationFlags` on Windows works along with
//! `JobObject`. By default does nothing.
//!
//! - **`fn post_spawn(&mut self, child: &mut tokio::process::Child, core: &CommandWrap)`** is
//! called after spawn, and should be used for any necessary cleanups. It is offered for
//! completeness but is expected to be less used than `wrap_child()`. By default does nothing.
//!
//! - **`fn wrap_child(&mut self, child: Box<dyn TokioChildWrapper>, core: &CommandWrap)`** is
//! called after all `post_spawn()`s have run. If your wrapper needs to override the methods on
//! Child, then it should create an instance of its own type implementing `TokioChildWrapper` and
//! return it here. Child wraps are _in order_: you may end up with a `Foo(Bar(Child))` or a
//! `Bar(Foo(Child))` depending on if `.wrap(Foo).wrap(Bar)` or `.wrap(Bar).wrap(Foo)` was called.
//! If your functionality is order-dependent, make sure to specify so in your documentation! By
//! default does nothing: no wrapping is performed and the input `child` is returned as-is.
//!
//! ## An Example Logging Wrapper
//!
//! Let's implement a logging wrapper that redirects a `Command`'s `stdout` and `stderr` into a
//! text file. We can use `std::io::pipe` to merge `stdout` and `stderr` into one channel, then
//! `std::io::copy` in a background thread to non-blockingly stream that data to disk as it comes
//! in.
//!
//! ```rust
//! # use process_wrap::std::{CommandWrap, CommandWrapper};
//! # use std::{fs::File, io, path::PathBuf, process::Command, thread};
//! #[derive(Debug)]
//! struct LogFile {
//! path: PathBuf,
//! }
//!
//! impl LogFile {
//! fn new(path: impl Into<PathBuf>) -> Self {
//! Self { path: path.into() }
//! }
//! }
//!
//! impl CommandWrapper for LogFile {
//! fn pre_spawn(&mut self, command: &mut Command, _core: &CommandWrap) -> io::Result<()> {
//! let mut logfile = File::create(&self.path)?;
//! let (mut rx, tx) = io::pipe()?;
//!
//! thread::spawn(move || {
//! io::copy(&mut rx, &mut logfile).unwrap();
//! });
//!
//! command.stdout(tx.try_clone()?).stderr(tx);
//! Ok(())
//! }
//! }
//! ```
//!
//! That's a great start, but it's actually introduced a resource leak: if the main thread of your
//! program exits before that background one does, then the background thread won't get a chance to
//! call `logfile`'s `Drop` implementation which closes the file. The file handle will be left open!
//! To fix this, we'll need to keep track of the background thread's `ThreadHandle` and `.join()` it
//! when calling `.wait()` on the `ChildWrapper`.
//!
//! ```rust
//! # use process_wrap::std::{ChildWrapper, CommandWrap, CommandWrapper};
//! # use std::{
//! # fs::File,
//! # io, mem,
//! # path::PathBuf,
//! # process::{Command, ExitStatus},
//! # thread::{self, JoinHandle},
//! # };
//! #[derive(Debug)]
//! struct LogFile {
//! path: PathBuf,
//! thread: Option<JoinHandle<()>>,
//! }
//!
//! impl LogFile {
//! fn new(path: impl Into<PathBuf>) -> Self {
//! Self {
//! path: path.into(),
//! thread: None,
//! }
//! }
//! }
//!
//! impl CommandWrapper for LogFile {
//! fn pre_spawn(&mut self, command: &mut Command, _core: &CommandWrap) -> io::Result<()> {
//! let mut logfile = File::create(&self.path)?;
//! let (mut rx, tx) = io::pipe()?;
//!
//! self.thread = Some(thread::spawn(move || {
//! io::copy(&mut rx, &mut logfile).unwrap();
//! }));
//!
//! command.stdout(tx.try_clone()?).stderr(tx);
//! Ok(())
//! }
//!
//! fn wrap_child(
//! &mut self,
//! child: Box<dyn ChildWrapper>,
//! _core: &CommandWrap,
//! ) -> io::Result<Box<dyn ChildWrapper>> {
//! let wrapped_child = LogFileChild {
//! inner: child,
//! thread: mem::take(&mut self.thread),
//! };
//! Ok(Box::new(wrapped_child))
//! }
//! }
//!
//! #[derive(Debug)]
//! struct LogFileChild {
//! inner: Box<dyn ChildWrapper>,
//! thread: Option<JoinHandle<()>>,
//! }
//!
//! impl ChildWrapper for LogFileChild {
//! fn inner(&self) -> &dyn ChildWrapper {
//! &*self.inner
//! }
//!
//! fn inner_mut(&mut self) -> &mut dyn ChildWrapper {
//! &mut *self.inner
//! }
//!
//! fn into_inner(self: Box<Self>) -> Box<dyn ChildWrapper> {
//! self.inner
//! }
//!
//! fn wait(&mut self) -> io::Result<ExitStatus> {
//! let exit_status = self.inner.wait();
//!
//! if let Some(thread) = mem::take(&mut self.thread) {
//! thread.join().unwrap();
//! }
//!
//! exit_status
//! }
//! }
//! ```
//!
//! Now we're cleaning up after ourselves, but there is one last issue: if you actually call
//! `.wait()`, then your program will deadlock! This is because `io::copy` copies data until `rx`
//! returns an EOF, but that only happens after *all* copies of `tx` are dropped. Currently, our
//! `Command` is holding onto `tx` even after calling `.spawn()`, so unless we manually drop the
//! `Command` (freeing both copies of `tx`) before calling `.wait()`, our program will deadlock!
//! We can fix this by telling `Command` to drop `tx` right after spawning the child — by this
//! point, the `ChildWrapper` will have already inherited the copies of `tx` that it needs, so
//! dropping `tx` from `Command` should be totally safe. We'll get `Command` to "drop" `tx` by
//! setting its `stdin` and `stdout` to `Stdio::null()` in `CommandWrapper::post_spawn()`.
//!
//! ```rust
//! # use process_wrap::std::{CommandWrap, CommandWrapper};
//! # use std::{
//! # io,
//! # path::PathBuf,
//! # process::{Child, Command, Stdio},
//! # thread::JoinHandle,
//! # };
//! # #[derive(Debug)]
//! # struct LogFile {
//! # path: PathBuf,
//! # thread: Option<JoinHandle<()>>,
//! # }
//! #
//! impl CommandWrapper for LogFile {
//! // ... snip ...
//! fn post_spawn(
//! &mut self,
//! command: &mut Command,
//! _child: &mut Child,
//! _core: &CommandWrap,
//! ) -> io::Result<()> {
//! command.stdout(Stdio::null()).stderr(Stdio::null());
//!
//! Ok(())
//! }
//! // ... snip ...
//! }
//! ```
//!
//! Finally, we can test that our new command-wrapper works:
//!
//! ```rust
//! # use process_wrap::std::{ChildWrapper, CommandWrap, CommandWrapper};
//! # use std::{
//! # error::Error,
//! # fs::{self, File},
//! # io, mem,
//! # path::PathBuf,
//! # process::{Child, Command, ExitStatus, Stdio},
//! # thread::{self, JoinHandle},
//! # };
//! # use tempfile::NamedTempFile;
//! # #[derive(Debug)]
//! # struct LogFile {
//! # path: PathBuf,
//! # thread: Option<JoinHandle<()>>,
//! # }
//! #
//! # impl LogFile {
//! # fn new(path: impl Into<PathBuf>) -> Self {
//! # Self {
//! # path: path.into(),
//! # thread: None,
//! # }
//! # }
//! # }
//! #
//! # impl CommandWrapper for LogFile {
//! # fn pre_spawn(&mut self, command: &mut Command, _core: &CommandWrap) -> io::Result<()> {
//! # let mut logfile = File::create(&self.path)?;
//! # let (mut rx, tx) = io::pipe()?;
//! #
//! # self.thread = Some(thread::spawn(move || {
//! # io::copy(&mut rx, &mut logfile).unwrap();
//! # }));
//! #
//! # command.stdout(tx.try_clone()?).stderr(tx);
//! # Ok(())
//! # }
//! #
//! # fn post_spawn(
//! # &mut self,
//! # command: &mut Command,
//! # _child: &mut Child,
//! # _core: &CommandWrap,
//! # ) -> io::Result<()> {
//! # command.stdout(Stdio::null()).stderr(Stdio::null());
//! #
//! # Ok(())
//! # }
//! #
//! # fn wrap_child(
//! # &mut self,
//! # child: Box<dyn ChildWrapper>,
//! # _core: &CommandWrap,
//! # ) -> io::Result<Box<dyn ChildWrapper>> {
//! # let wrapped_child = LogFileChild {
//! # inner: child,
//! # thread: mem::take(&mut self.thread),
//! # };
//! # Ok(Box::new(wrapped_child))
//! # }
//! # }
//! #
//! # #[derive(Debug)]
//! # struct LogFileChild {
//! # inner: Box<dyn ChildWrapper>,
//! # thread: Option<JoinHandle<()>>,
//! # }
//! #
//! # impl ChildWrapper for LogFileChild {
//! # fn inner(&self) -> &dyn ChildWrapper {
//! # &*self.inner
//! # }
//! #
//! # fn inner_mut(&mut self) -> &mut dyn ChildWrapper {
//! # &mut *self.inner
//! # }
//! #
//! # fn into_inner(self: Box<Self>) -> Box<dyn ChildWrapper> {
//! # self.inner
//! # }
//! #
//! # fn wait(&mut self) -> io::Result<ExitStatus> {
//! # let exit_status = self.inner.wait();
//! #
//! # if let Some(thread) = mem::take(&mut self.thread) {
//! # thread.join().unwrap();
//! # }
//! #
//! # exit_status
//! # }
//! # }
//! #
//! fn main() -> Result<(), Box<dyn Error>> {
//! #[cfg(windows)]
//! let mut command = CommandWrap::with_new("cmd", |command| {
//! command.args(["/c", "echo Hello && echo World 1>&2"]);
//! });
//! #[cfg(unix)]
//! let mut command = CommandWrap::with_new("sh", |command| {
//! command.args(["-c", "echo Hello && echo World 1>&2"]);
//! });
//!
//! let logfile = NamedTempFile::new()?;
//! let logfile_path = logfile.path();
//!
//! command.wrap(LogFile::new(logfile_path)).spawn()?.wait()?;
//!
//! let logfile_lines: Vec<String> = fs::read_to_string(logfile_path)?
//! .lines()
//! .map(|l| l.trim().into())
//! .collect();
//! assert_eq!(logfile_lines, vec!["Hello", "World"]);
//!
//! Ok(())
//! }
//! ```
//!
//! # Features
//!
//! ## Frontends
//!
//! The default features do not enable a frontend, so you must choose one of the following:
//!
//! - `std`: enables the std-based API.
//! - `tokio1`: enables the Tokio-based API.
//!
//! Both can exist at the same time, but generally you'll want to use one or the other.
//!
//! ## Wrappers
//!
//! - `creation-flags`: **default**, enables the creation flags wrapper (Windows-only).
//! - `job-object`: **default**, enables the job object wrapper (Windows-only).
//! - `kill-on-drop`: **default**, enables the kill on drop wrapper (Tokio-only).
//! - `process-group`: **default**, enables the process group wrapper (Unix-only).
//! - `process-session`: **default**, enables the process session wrapper (Unix-only).
//! - `reset-sigmask`: enables the sigmask reset wrapper (Unix-only).
//!
pub
/// Internal memoization of the exit status of a child process.
// easier than listing exactly which featuresets use it
pub