io_process/runtimes/
tokio.rs1use std::{io, process::Output};
4
5use tokio::process::Command as TokioCommand;
6
7use crate::{command::Command, io::ProcessIo, status::SpawnStatus};
8
9pub async fn handle(io: ProcessIo) -> io::Result<ProcessIo> {
14 match io {
15 ProcessIo::SpawnThenWait(io) => spawn_then_wait(io).await,
16 ProcessIo::SpawnThenWaitWithOutput(io) => spawn_then_wait_with_output(io).await,
17 }
18}
19
20pub async fn spawn_then_wait(input: Result<SpawnStatus, Command>) -> io::Result<ProcessIo> {
26 let Err(command) = input else {
27 let kind = io::ErrorKind::InvalidInput;
28 return Err(io::Error::new(kind, "missing command"));
29 };
30
31 let mut command = TokioCommand::from(command);
32 let mut child = command.spawn()?;
33
34 #[cfg(unix)]
35 let stdin = child.stdin.take().and_then(|io| io.into_owned_fd().ok());
36 #[cfg(windows)]
37 let stdin = child
38 .stdin
39 .take()
40 .and_then(|io| io.into_owned_handle().ok());
41
42 #[cfg(unix)]
43 let stdout = child.stdout.take().and_then(|io| io.into_owned_fd().ok());
44 #[cfg(windows)]
45 let stdout = child
46 .stdout
47 .take()
48 .and_then(|io| io.into_owned_handle().ok());
49
50 #[cfg(unix)]
51 let stderr = child.stderr.take().and_then(|io| io.into_owned_fd().ok());
52 #[cfg(windows)]
53 let stderr = child
54 .stderr
55 .take()
56 .and_then(|io| io.into_owned_handle().ok());
57
58 let output = SpawnStatus {
59 status: child.wait().await?,
60 stdin: stdin.map(Into::into),
61 stdout: stdout.map(Into::into),
62 stderr: stderr.map(Into::into),
63 };
64
65 Ok(ProcessIo::SpawnThenWait(Ok(output)))
66}
67
68pub async fn spawn_then_wait_with_output(input: Result<Output, Command>) -> io::Result<ProcessIo> {
73 let Err(command) = input else {
74 let kind = io::ErrorKind::InvalidInput;
75 return Err(io::Error::new(kind, "missing command"));
76 };
77
78 let mut command = TokioCommand::from(command);
79 let output = command.output().await?;
80
81 Ok(ProcessIo::SpawnThenWaitWithOutput(Ok(output)))
82}
83
84impl From<Command> for TokioCommand {
86 fn from(builder: Command) -> Self {
87 let mut command = TokioCommand::new(&*builder.get_program());
88
89 if let Some(args) = builder.get_args() {
90 for arg in args {
91 command.arg(&*arg);
92 }
93 }
94
95 if let Some(envs) = builder.envs {
96 for (key, val) in envs {
97 command.env(key, val);
98 }
99 }
100
101 if let Some(dir) = builder.current_dir {
102 command.current_dir(dir);
103 }
104
105 if let Some(cfg) = builder.stdin {
106 command.stdin(cfg);
107 }
108
109 if let Some(cfg) = builder.stdout {
110 command.stdout(cfg);
111 }
112
113 if let Some(cfg) = builder.stderr {
114 command.stderr(cfg);
115 }
116
117 command
118 }
119}