Skip to content

Commit 9dfc8fc

Browse files
committed
Update FunChildren APIs
1 parent d551d31 commit 9dfc8fc

File tree

4 files changed

+91
-75
lines changed

4 files changed

+91
-75
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,8 +276,8 @@ fn my_cmd(env: &mut CmdEnv) -> CmdResult {
276276
--another-option yyy
277277
}?
278278
.wait_with_all();
279-
writeln!(env.stdout(), "{}", stdout?)?;
280-
writeln!(env.stderr(), "{}", stderr?)?;
279+
writeln!(env.stdout(), "{}", stdout)?;
280+
writeln!(env.stderr(), "{}", stderr)?;
281281
res
282282
}
283283

src/child.rs

Lines changed: 86 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::info;
1+
use crate::{info, warn};
22
use crate::{process, CmdResult, FunResult};
33
use os_pipe::PipeReader;
44
use std::io::{BufRead, BufReader, Error, ErrorKind, Read, Result};
@@ -77,24 +77,40 @@ pub struct FunChildren {
7777
}
7878

7979
impl FunChildren {
80-
/// Waits for the children processes to exit completely, returning the output.
80+
/// Waits for the children processes to exit completely, returning the command result, stdout
81+
/// content string and stderr content string.
82+
pub fn wait_with_all(&mut self) -> (CmdResult, String, String) {
83+
self.inner_wait_with_all(true)
84+
}
85+
86+
/// Waits for the children processes to exit completely, returning the stdout output.
8187
pub fn wait_with_output(&mut self) -> FunResult {
88+
let (res, stdout, _) = self.inner_wait_with_all(false);
89+
if let Err(e) = res {
90+
if !self.ignore_error {
91+
return Err(e);
92+
}
93+
}
94+
Ok(stdout)
95+
}
96+
97+
/// Waits for the children processes to exit completely, and read all bytes from stdout into `buf`.
98+
pub fn wait_with_raw_output(&mut self, buf: &mut Vec<u8>) -> CmdResult {
8299
// wait for the last child result
83100
let handle = self.children.pop().unwrap();
84-
let wait_last = handle.wait_with_output(self.ignore_error);
101+
let wait_last = handle.wait_with_raw_output(self.ignore_error, buf);
85102
match wait_last {
86103
Err(e) => {
87104
let _ = CmdChildren::wait_children(&mut self.children);
88105
Err(e)
89106
}
90-
Ok(output) => {
107+
Ok(_) => {
91108
let ret = CmdChildren::wait_children(&mut self.children);
92-
if let Err(e) = ret {
93-
if !self.ignore_error {
94-
return Err(e);
95-
}
109+
if self.ignore_error {
110+
Ok(())
111+
} else {
112+
ret
96113
}
97-
Ok(output)
98114
}
99115
}
100116
}
@@ -127,19 +143,23 @@ impl FunChildren {
127143
CmdChildren::wait_children(&mut self.children)
128144
}
129145

130-
/// Waits for the children processes to exit completely, returning the command result, stdout
131-
/// read result and stderr read result.
132-
pub fn wait_with_all(&mut self) -> (CmdResult, FunResult, FunResult) {
146+
/// Returns the OS-assigned process identifiers associated with these children processes.
147+
pub fn pids(&self) -> Vec<u32> {
148+
self.children.iter().filter_map(|x| x.pid()).collect()
149+
}
150+
151+
fn inner_wait_with_all(&mut self, capture_stderr: bool) -> (CmdResult, String, String) {
133152
// wait for the last child result
134153
let handle = self.children.pop().unwrap();
135-
let wait_all = handle.wait_with_all(true);
154+
let mut stdout_buf = Vec::new();
155+
let mut stderr = String::new();
156+
let res = handle.wait_with_all(capture_stderr, &mut stdout_buf, &mut stderr);
136157
let _ = CmdChildren::wait_children(&mut self.children);
137-
wait_all
138-
}
139-
140-
/// Returns the OS-assigned process identifiers associated with these children processes
141-
pub fn pids(&self) -> Vec<u32> {
142-
self.children.iter().filter_map(|x| x.pid()).collect()
158+
let mut stdout: String = String::from_utf8_lossy(&stdout_buf).into();
159+
if stdout.ends_with('\n') {
160+
stdout.pop();
161+
}
162+
(res, stdout, stderr)
143163
}
144164
}
145165

@@ -183,45 +203,41 @@ impl CmdChild {
183203
Ok(())
184204
}
185205

186-
fn wait_with_output(self, ignore_error: bool) -> FunResult {
187-
let (res, stdout, _) = self.wait_with_all(false);
188-
if !ignore_error {
189-
res?;
206+
fn wait_with_raw_output(self, ignore_error: bool, stdout_buf: &mut Vec<u8>) -> CmdResult {
207+
let mut _stderr = String::new();
208+
let res = self.wait_with_all(false, stdout_buf, &mut _stderr);
209+
if ignore_error {
210+
return Ok(());
190211
}
191-
stdout
212+
res
192213
}
193214

194-
fn wait_with_all(mut self, capture: bool) -> (CmdResult, FunResult, FunResult) {
215+
fn wait_with_all(
216+
mut self,
217+
capture_stderr: bool,
218+
stdout_buf: &mut Vec<u8>,
219+
stderr_buf: &mut String,
220+
) -> CmdResult {
195221
let mut stderr_thread = StderrThread::new(
196222
&self.cmd,
197223
&self.file,
198224
self.line,
199225
self.stderr.take(),
200-
capture,
226+
capture_stderr,
201227
);
202-
let stdout_output = {
203-
if let Some(mut out) = self.stdout.take() {
204-
let mut s = String::new();
205-
match out.read_to_string(&mut s) {
206-
Err(e) => Err(e),
207-
Ok(_) => {
208-
if s.ends_with('\n') {
209-
s.pop();
210-
}
211-
Ok(s)
212-
}
213-
}
214-
} else {
215-
Ok("".into())
228+
let mut stdout_res = Ok(());
229+
if let Some(mut stdout) = self.stdout.take() {
230+
if let Err(e) = stdout.read_to_end(stdout_buf) {
231+
stdout_res = Err(e)
216232
}
217-
};
218-
let stderr_output = stderr_thread.join();
219-
let res = self.handle.wait(&self.cmd, &self.file, self.line);
220-
(res, stdout_output, stderr_output)
233+
}
234+
*stderr_buf = stderr_thread.join();
235+
let wait_res = self.handle.wait(&self.cmd, &self.file, self.line);
236+
wait_res.and(stdout_res)
221237
}
222238

223239
fn kill(self) -> CmdResult {
224-
self.handle.kill()
240+
self.handle.kill(&self.cmd, &self.file, self.line)
225241
}
226242

227243
fn pid(&self) -> Option<u32> {
@@ -244,12 +260,7 @@ impl CmdChildHandle {
244260
Err(e) => return Err(process::new_cmd_io_error(&e, cmd, file, line)),
245261
Ok(status) => {
246262
if !status.success() {
247-
return Err(Self::status_to_io_error(
248-
status,
249-
&format!("Running [{cmd}] exited with error"),
250-
file,
251-
line,
252-
));
263+
return Err(Self::status_to_io_error(status, cmd, file, line));
253264
}
254265
}
255266
}
@@ -277,26 +288,34 @@ impl CmdChildHandle {
277288
Ok(())
278289
}
279290

280-
fn status_to_io_error(status: ExitStatus, run_cmd: &str, file: &str, line: u32) -> Error {
291+
fn status_to_io_error(status: ExitStatus, cmd: &str, file: &str, line: u32) -> Error {
281292
if let Some(code) = status.code() {
282293
Error::new(
283294
ErrorKind::Other,
284-
format!("{run_cmd}; status code: {code} at {file}:{line}"),
295+
format!("Running [{cmd}] exited with error; status code: {code} at {file}:{line}"),
285296
)
286297
} else {
287298
Error::new(
288299
ErrorKind::Other,
289-
format!("{run_cmd}; terminated by {status} at {file}:{line}"),
300+
format!(
301+
"Running [{cmd}] exited with error; terminated by {status} at {file}:{line}"
302+
),
290303
)
291304
}
292305
}
293306

294-
fn kill(self) -> CmdResult {
307+
fn kill(self, cmd: &str, file: &str, line: u32) -> CmdResult {
295308
match self {
296-
CmdChildHandle::Proc(mut proc) => proc.kill(),
297-
CmdChildHandle::Thread(_thread) => {
298-
panic!("thread killing not suppported!")
299-
}
309+
CmdChildHandle::Proc(mut proc) => proc.kill().map_err(|e| {
310+
Error::new(
311+
e.kind(),
312+
format!("Killing process [{cmd}] failed with error: {e} at {file}:{line}"),
313+
)
314+
}),
315+
CmdChildHandle::Thread(_thread) => Err(Error::new(
316+
ErrorKind::Other,
317+
format!("Killing thread [{cmd}] failed: not supported at {file}:{line}"),
318+
)),
300319
CmdChildHandle::SyncFn => Ok(()),
301320
}
302321
}
@@ -352,27 +371,24 @@ impl StderrThread {
352371
}
353372
}
354373

355-
fn join(&mut self) -> FunResult {
374+
fn join(&mut self) -> String {
356375
if let Some(thread) = self.thread.take() {
357376
match thread.join() {
358377
Err(e) => {
359-
return Err(Error::new(
360-
ErrorKind::Other,
361-
format!(
362-
"Running [{}] stderr thread joined with error: {:?} at {}:{}",
363-
self.cmd, e, self.file, self.line
364-
),
365-
));
378+
warn!(
379+
"Running [{}] stderr thread joined with error: {:?} at {}:{}",
380+
self.cmd, e, self.file, self.line
381+
);
366382
}
367-
Ok(output) => return Ok(output),
383+
Ok(output) => return output,
368384
}
369385
}
370-
Ok("".into())
386+
"".into()
371387
}
372388
}
373389

374390
impl Drop for StderrThread {
375391
fn drop(&mut self) {
376-
let _ = self.join();
392+
self.join();
377393
}
378394
}

src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,8 +304,8 @@
304304
//! --another-option yyy
305305
//! }?
306306
//! .wait_with_all();
307-
//! writeln!(env.stdout(), "{}", stdout?)?;
308-
//! writeln!(env.stderr(), "{}", stderr?)?;
307+
//! writeln!(env.stdout(), "{}", stdout)?;
308+
//! writeln!(env.stderr(), "{}", stderr)?;
309309
//! res
310310
//! }
311311
//!

src/process.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,7 @@ impl Cmd {
496496
"{CD_CMD}: missing directory at {file}:{line}",
497497
));
498498
} else if self.args.len() > 2 {
499-
let err_msg = format!("{CD_CMD}: too many arguments");
499+
let err_msg = format!("{CD_CMD}: too many arguments at {file}:{line}");
500500
return Err(Error::new(ErrorKind::Other, err_msg));
501501
}
502502

0 commit comments

Comments
 (0)