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
//! Subprocess handling event source for the [Calloop event loop][calloop].
//!
//! [calloop]: https://crates.io/crates/calloop
//!
//! Calloop is built around the concept of *event sources* eg. timers, channels,
//! file descriptors themselves, which generate events and call a callback for
//! them. This crate provides two kinds of event sources that let you manage
//! subprocess:
//! - a [chain], which runs a series of commands and generates a success/failure
//! event
//! - a [listener], which runs a single command and generates an event based on
//! lines of output
//!
//! [chain]: crate::SubprocChain
//! [listener]: crate::SubprocListen
//!
//! # Error handling
//!
//! Errors in this crate are classified into two high-level kinds, as is the
//! [custom for Calloop][calloop-errors].
//!
//! [calloop-errors]: https://smithay.github.io/calloop/ch02-06-errors.html
//!
//! [`LaunchError`] might be generated by an event source's [`process_events()`]
//! method when a critical error is encountered. This generally means that the
//! event source itself can't continue sensibly and should be removed from the
//! event loop.
//!
//! The [`ErrorEvent`] type is concerned with errors of the subprocess it's
//! managing, including the command not being found or requiring permissions
//! that the process doesn't have. These will be contained in the type of event
//! that is given to the callback.
//!
//! The main way that these are different is: if there's a problem using
//! whatever underlying mechanism we use to spawn a subprocess or get results
//! back from it, then a [`LaunchError`] is produced then and there. But if the
//! underlying mechanics for setting up a subprocess succeed, and it's just that
//! the command contains a typo and can't be found, then it's perfectly possible
//! to generate an event for that. It will just happen to contain the [IO error]
//! you'd expect when trying to run the command directly.
//!
//! [IO error]: std::io::Error
//!
//! # Releasing file descriptors
//!
//! To avoid "holding on" to file descriptors (and just the memory associated
//! with the event source itself), you **must** honour the
//! [`calloop::PostAction`] returned from [`process_events()`] and remove the
//! event source if requested.
//!
//! This is automatically done for top level event sources ie. those added by
//! [`insert_source()`]. If you use it as part of a composed event source, you
//! must either manage reregistration yourself, or wrap the source with
//! [`TransientSource`](calloop::transient::TransientSource).
//!
//! [`insert_source()`]: calloop::LoopHandle::insert_source()
//!
//! # Example
//!
//! This runs `ls -a` and prints the events received.
//!
//! ```
//! use calloop::{EventLoop, LoopSignal};
//! use calloop_subproc::{Command, SubprocListen};
//!
//! let ls_cmd = Command::new("ls").with_args(["-a"]);
//! let listener = SubprocListen::new(ls_cmd).unwrap();
//!
//! let mut event_loop: EventLoop<LoopSignal> = EventLoop::try_new().unwrap();
//!
//! event_loop
//! .handle()
//! .insert_source(listener, |event, _, stopper| {
//! // What kind of event did we get this time?
//! let msg = match event {
//! // The subprocess was just started.
//! calloop_subproc::ListenEvent::Start => "Subprocess started".to_owned(),
//! // We got a line of output from the subprocess.
//! calloop_subproc::ListenEvent::Line(line) => format!("Output: {}", line),
//! // The subprocess ended.
//! calloop_subproc::ListenEvent::End(res) => {
//! // Since the subprocess ended, we want to stop the loop.
//! stopper.stop();
//!
//! // Show why the subprocess ended.
//! match res {
//! Ok(()) => "Subprocess completed".to_owned(),
//! Err(error) => format!("Subprocess error: {:?}", error),
//! }
//! }
//! };
//!
//! // Print our formatted event.
//! println!("{}", msg);
//!
//! // This callback must return true if the subprocess should be
//! // killed. We want it to run to completion, so we return false.
//! false
//! })
//! .unwrap();
//!
//! event_loop
//! .run(None, &mut event_loop.get_signal(), |_| {})
//! .unwrap();
//! ```
//!
//! [`process_events()`]: calloop::EventSource::process_events
pub use SubprocChain;
pub use ;
use *;
use OsString;
use fmt;
/// This is the error type that our event source's callback might receive. If
/// something went wrong, it could be for one of two reasons:
/// - the subprocess returned a non-zero status: `SubprocError(status)`
/// - there was an IO error handling the subprocess: `IoError(ioerror)`
/// This is the error type that might be returned from the methods on the event
/// source itself. In general they are more "critical" than [`ErrorEvent`] and
/// mean that the event source is unable to continue.
type SrcResult = Result;
/// There's a lot of common manipulation of what we think of as a command. This
/// type factors out some of that, specifically:
/// - conversion from an arbitrary sequence of strings
/// - conversion to async_process's builder type
/// - debugging representation
/// Running a command can result in either `Ok(())` or the error outcomes
/// described above (see `SrcResult`). The `command` is actually just a
/// `String`: the debug representation of the `Command`. This exists just so we
/// can log what command failed and why.