unistore_process/
child.rs1use crate::error::ProcessError;
6use crate::output::ProcessOutput;
7use std::time::Duration;
8use tokio::io::{AsyncReadExt, AsyncWriteExt};
9use tokio::process::Child as TokioChild;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub struct ExitStatus {
14 code: Option<i32>,
15}
16
17impl ExitStatus {
18 pub fn from_code(code: i32) -> Self {
20 Self { code: Some(code) }
21 }
22
23 #[cfg(unix)]
25 pub fn from_signal(signal: i32) -> Self {
26 Self { code: Some(128 + signal) }
27 }
28
29 pub fn unknown() -> Self {
31 Self { code: None }
32 }
33
34 pub fn success(&self) -> bool {
36 self.code == Some(0)
37 }
38
39 pub fn code(&self) -> Option<i32> {
41 self.code
42 }
43}
44
45impl From<std::process::ExitStatus> for ExitStatus {
46 fn from(status: std::process::ExitStatus) -> Self {
47 Self {
48 code: status.code(),
49 }
50 }
51}
52
53pub struct Child {
55 inner: TokioChild,
56 pid: u32,
57}
58
59impl Child {
60 pub(crate) fn new(child: TokioChild) -> Result<Self, ProcessError> {
62 let pid = child
63 .id()
64 .ok_or_else(|| ProcessError::SpawnFailed("无法获取进程 ID".into()))?;
65
66 Ok(Self { inner: child, pid })
67 }
68
69 pub fn id(&self) -> u32 {
71 self.pid
72 }
73
74 pub fn is_running(&mut self) -> bool {
76 matches!(self.inner.try_wait(), Ok(None))
77 }
78
79 pub async fn wait(&mut self) -> Result<ExitStatus, ProcessError> {
81 let status = self.inner.wait().await?;
82 Ok(status.into())
83 }
84
85 pub async fn wait_timeout(&mut self, timeout: Duration) -> Result<Option<ExitStatus>, ProcessError> {
87 match tokio::time::timeout(timeout, self.inner.wait()).await {
88 Ok(Ok(status)) => Ok(Some(status.into())),
89 Ok(Err(e)) => Err(ProcessError::from(e)),
90 Err(_) => Ok(None), }
92 }
93
94 pub async fn wait_with_output(self) -> Result<ProcessOutput, ProcessError> {
96 let output = self.inner.wait_with_output().await?;
97 Ok(ProcessOutput::new(
98 output.status.into(),
99 output.stdout,
100 output.stderr,
101 ))
102 }
103
104 pub async fn write_stdin(&mut self, data: &[u8]) -> Result<(), ProcessError> {
106 if let Some(stdin) = self.inner.stdin.as_mut() {
107 stdin.write_all(data).await?;
108 stdin.flush().await?;
109 Ok(())
110 } else {
111 Err(ProcessError::IoError("标准输入未捕获".into()))
112 }
113 }
114
115 pub fn close_stdin(&mut self) {
117 self.inner.stdin.take();
118 }
119
120 pub async fn read_stdout(&mut self) -> Result<Vec<u8>, ProcessError> {
122 if let Some(stdout) = self.inner.stdout.as_mut() {
123 let mut buf = Vec::new();
124 stdout.read_to_end(&mut buf).await?;
125 Ok(buf)
126 } else {
127 Err(ProcessError::IoError("标准输出未捕获".into()))
128 }
129 }
130
131 pub async fn read_stderr(&mut self) -> Result<Vec<u8>, ProcessError> {
133 if let Some(stderr) = self.inner.stderr.as_mut() {
134 let mut buf = Vec::new();
135 stderr.read_to_end(&mut buf).await?;
136 Ok(buf)
137 } else {
138 Err(ProcessError::IoError("标准错误未捕获".into()))
139 }
140 }
141
142 #[cfg(unix)]
144 pub fn terminate(&self) -> Result<(), ProcessError> {
145 use nix::sys::signal::{kill, Signal};
146 use nix::unistd::Pid;
147
148 kill(Pid::from_raw(self.pid as i32), Signal::SIGTERM)
149 .map_err(|e| ProcessError::SignalFailed(e.to_string()))
150 }
151
152 #[cfg(windows)]
154 pub fn terminate(&mut self) -> Result<(), ProcessError> {
155 self.inner
156 .start_kill()
157 .map_err(|e| ProcessError::SignalFailed(e.to_string()))
158 }
159
160 pub fn kill(&mut self) -> Result<(), ProcessError> {
162 self.inner
163 .start_kill()
164 .map_err(|e| ProcessError::SignalFailed(e.to_string()))
165 }
166
167 pub fn inner_mut(&mut self) -> &mut TokioChild {
169 &mut self.inner
170 }
171}
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176
177 #[test]
178 fn test_exit_status() {
179 let status = ExitStatus::from_code(0);
180 assert!(status.success());
181 assert_eq!(status.code(), Some(0));
182
183 let status = ExitStatus::from_code(1);
184 assert!(!status.success());
185 assert_eq!(status.code(), Some(1));
186
187 let status = ExitStatus::unknown();
188 assert!(!status.success());
189 assert_eq!(status.code(), None);
190 }
191
192 #[tokio::test]
193 async fn test_simple_command() {
194 use crate::command::Command;
195
196 #[cfg(windows)]
197 let output = Command::new("cmd")
198 .args(["/C", "echo", "hello"])
199 .output()
200 .await
201 .unwrap();
202
203 #[cfg(unix)]
204 let output = Command::new("echo")
205 .arg("hello")
206 .output()
207 .await
208 .unwrap();
209
210 assert!(output.success());
211 assert!(output.stdout_lossy().contains("hello"));
212 }
213}