|
1 | 1 | use crate::error::Error;
|
| 2 | +use futures::Future; |
2 | 3 | use librespot_playback::player::PlayerEvent;
|
3 | 4 | use log::info;
|
4 | 5 | use std::{
|
5 | 6 | collections::HashMap,
|
6 |
| - io::{self, Read, Write}, |
7 |
| - process::{Command, ExitStatus, Stdio}, |
| 7 | + io::{self, Write}, |
| 8 | + pin::Pin, |
| 9 | + process::{Output, Stdio}, |
| 10 | + task::{Context, Poll}, |
8 | 11 | };
|
| 12 | +use tokio::process::Command; |
9 | 13 |
|
10 | 14 | /// Blocks while provided command is run in a subprocess using the provided
|
11 | 15 | /// shell. If successful, returns the contents of the subprocess's `stdout` as a
|
12 | 16 | /// `String`.
|
13 | 17 | pub(crate) fn run_program(shell: &str, cmd: &str) -> Result<String, Error> {
|
14 | 18 | info!("Running {:?} using {:?}", cmd, shell);
|
15 |
| - let output = Command::new(shell) |
| 19 | + let output = std::process::Command::new(shell) |
16 | 20 | .arg("-c")
|
17 | 21 | .arg(cmd)
|
18 | 22 | .output()
|
@@ -150,96 +154,69 @@ pub(crate) fn spawn_program_on_event(
|
150 | 154 | spawn_program(shell, cmd, env)
|
151 | 155 | }
|
152 | 156 |
|
153 |
| -/// Same as a `std::process::Child` except when this `Child` exits: |
| 157 | +/// Wraps a process into a Future that executes something after the process has |
| 158 | +/// exited: |
154 | 159 | /// * successfully: It writes the contents of it's stdout to the stdout of the
|
155 | 160 | /// main process.
|
156 | 161 | /// * unsuccesfully: It returns an error that includes the contents it's stderr
|
157 | 162 | /// as well as information on the command that was run and the shell that
|
158 | 163 | /// invoked it.
|
159 |
| -#[derive(Debug)] |
160 | 164 | pub(crate) struct Child {
|
161 | 165 | cmd: String,
|
162 |
| - inner: std::process::Child, |
| 166 | + future: Pin<Box<dyn Future<Output = io::Result<Output>>>>, |
163 | 167 | shell: String,
|
164 | 168 | }
|
165 | 169 |
|
166 | 170 | impl Child {
|
167 |
| - pub(crate) fn new(cmd: String, child: std::process::Child, shell: String) -> Self { |
| 171 | + pub(crate) fn new(cmd: String, child: tokio::process::Child, shell: String) -> Self { |
168 | 172 | Self {
|
169 | 173 | cmd,
|
170 |
| - inner: child, |
| 174 | + future: Box::pin(child.wait_with_output()), |
171 | 175 | shell,
|
172 | 176 | }
|
173 | 177 | }
|
| 178 | +} |
174 | 179 |
|
175 |
| - #[allow(unused)] |
176 |
| - pub(crate) fn wait(&mut self) -> Result<(), Error> { |
177 |
| - match self.inner.wait() { |
178 |
| - Ok(status) => { |
179 |
| - self.write_output(status)?; |
180 |
| - Ok(()) |
181 |
| - } |
182 |
| - Err(e) => Err(Error::subprocess_with_err(&self.shell, &self.cmd, e)), |
183 |
| - } |
184 |
| - } |
185 |
| - |
186 |
| - #[allow(unused)] |
187 |
| - pub(crate) fn try_wait(&mut self) -> Result<Option<()>, Error> { |
188 |
| - match self.inner.try_wait() { |
189 |
| - Ok(Some(status)) => { |
190 |
| - self.write_output(status)?; |
191 |
| - Ok(Some(())) |
| 180 | +impl Future for Child { |
| 181 | + type Output = Result<(), Error>; |
| 182 | + |
| 183 | + fn poll(mut self: Pin<&mut Child>, cx: &mut Context<'_>) -> Poll<Self::Output> { |
| 184 | + if let Poll::Ready(result) = self.future.as_mut().poll(cx) { |
| 185 | + let output = match result { |
| 186 | + Ok(output) => output, |
| 187 | + Err(err) => { |
| 188 | + return Poll::Ready(Err(Error::subprocess_with_err( |
| 189 | + &self.shell, |
| 190 | + &self.cmd, |
| 191 | + err, |
| 192 | + ))); |
| 193 | + } |
| 194 | + }; |
| 195 | + |
| 196 | + if output.status.success() { |
| 197 | + // If successful, write subprocess's stdout to main process's stdout... |
| 198 | + let stdout = io::stdout(); |
| 199 | + let mut writer = stdout.lock(); |
| 200 | + |
| 201 | + writer |
| 202 | + .write_all(&output.stdout) |
| 203 | + .map_err(|e| Error::subprocess_with_err(&self.shell, &self.cmd, e))?; |
| 204 | + |
| 205 | + writer |
| 206 | + .flush() |
| 207 | + .map_err(|e| Error::subprocess_with_err(&self.shell, &self.cmd, e))?; |
| 208 | + |
| 209 | + Poll::Ready(Ok(())) |
| 210 | + } else { |
| 211 | + // If unsuccessful, return an error that includes the contents of stderr... |
| 212 | + let stderr = String::from_utf8(output.stderr); |
| 213 | + match stderr { |
| 214 | + Ok(stderr) => Err(Error::subprocess_with_str(&self.shell, &self.cmd, &stderr)), |
| 215 | + Err(_) => Err(Error::subprocess(&self.shell, &self.cmd)), |
| 216 | + }? |
192 | 217 | }
|
193 |
| - Ok(None) => Ok(None), |
194 |
| - Err(e) => Err(Error::subprocess_with_err(&self.shell, &self.cmd, e)), |
195 |
| - } |
196 |
| - } |
197 |
| - |
198 |
| - fn write_output(&mut self, status: ExitStatus) -> Result<(), Error> { |
199 |
| - if status.success() { |
200 |
| - // If successful, write subprocess's stdout to main process's stdout... |
201 |
| - let mut stdout_of_child = self.inner.stdout.as_mut().unwrap(); |
202 |
| - let reader = &mut stdout_of_child; |
203 |
| - |
204 |
| - let stdout_of_main = io::stdout(); |
205 |
| - let mut guard = stdout_of_main.lock(); |
206 |
| - let writer = &mut guard; |
207 |
| - |
208 |
| - io::copy(reader, writer) |
209 |
| - .map_err(|e| Error::subprocess_with_err(&self.shell, &self.cmd, e))?; |
210 |
| - |
211 |
| - writer |
212 |
| - .flush() |
213 |
| - .map_err(|e| Error::subprocess_with_err(&self.shell, &self.cmd, e))?; |
214 |
| - |
215 |
| - Ok(()) |
216 | 218 | } else {
|
217 |
| - // If unsuccessful, return an error that includes the contents of stderr... |
218 |
| - let mut buf = String::new(); |
219 |
| - match self.inner.stderr.as_mut().unwrap().read_to_string(&mut buf) { |
220 |
| - Ok(_nread) => Err(Error::subprocess_with_str(&self.shell, &self.cmd, &buf)), |
221 |
| - Err(e) => Err(Error::subprocess_with_err(&self.shell, &self.cmd, e)), |
222 |
| - } |
| 219 | + Poll::Pending |
223 | 220 | }
|
224 | 221 | }
|
225 | 222 | }
|
226 |
| - |
227 |
| -impl std::ops::Deref for Child { |
228 |
| - type Target = std::process::Child; |
229 |
| - |
230 |
| - fn deref(&self) -> &Self::Target { |
231 |
| - &self.inner |
232 |
| - } |
233 |
| -} |
234 |
| - |
235 |
| -impl std::ops::DerefMut for Child { |
236 |
| - fn deref_mut(&mut self) -> &mut Self::Target { |
237 |
| - &mut self.inner |
238 |
| - } |
239 |
| -} |
240 |
| - |
241 |
| -impl From<Child> for std::process::Child { |
242 |
| - fn from(child: Child) -> Self { |
243 |
| - child.inner |
244 |
| - } |
245 |
| -} |
0 commit comments