From cce0b5ec41d84aee8e2c4413eed3cba17f17c376 Mon Sep 17 00:00:00 2001 From: askiiart Date: Fri, 10 Jan 2025 10:37:51 -0600 Subject: [PATCH 1/2] clean up code --- src/lib.rs | 52 ++++++++++++++++------------------------------------ 1 file changed, 16 insertions(+), 36 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d0313e2..1f67dd2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,46 +19,26 @@ pub struct CmdOutput { impl CmdOutput { /// Returns only stdout pub fn stdout(self) -> Option> { - match self.lines { - Some(lines) => { - return Some( - lines - .into_iter() - .filter(|l| { - if l.printed_to == LineType::Stdout { - return true; - } - return false; - }) - .collect(), - ); - } - None => { - return None; - } - } + self.lines.and_then(|lines| { + Some( + lines + .into_iter() + .filter(|line| line.printed_to == LineType::Stdout) + .collect(), + ) + }) } /// Returns only stdout pub fn stderr(self) -> Option> { - match self.lines { - Some(lines) => { - return Some( - lines - .into_iter() - .filter(|l| { - if l.printed_to == LineType::Stderr { - return true; - } - return false; - }) - .collect(), - ); - } - None => { - return None; - } - } + self.lines.and_then(|lines| { + Some( + lines + .into_iter() + .filter(|line| line.printed_to == LineType::Stderr) + .collect(), + ) + }) } /// Returns all output From 1f1070b90c7410a482d50d8a3d3ee5a71cf11e2c Mon Sep 17 00:00:00 2001 From: askiiart Date: Fri, 10 Jan 2025 16:32:36 -0600 Subject: [PATCH 2/2] rename run_with_funcs -> run_funcs, add run_funcs_with_lines for similar functionality --- src/lib.rs | 41 +++++++++++++++++++++++++++++++- src/tests.rs | 67 ++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 100 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1f67dd2..baae4da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -195,7 +195,7 @@ pub fn run(command: &mut Command) -> CmdOutput { }; } -pub fn run_with_funcs( +pub fn run_funcs( command: &mut Command, stdout_func: impl Fn(Lines>) -> () + std::marker::Send + 'static, stderr_func: impl Fn(Lines>) -> () + std::marker::Send + 'static, @@ -231,3 +231,42 @@ pub fn run_with_funcs( duration: end.duration_since(start), }; } + +pub fn run_funcs_with_lines( + command: &mut Command, + stdout_func: impl Fn(Lines>) -> Vec + std::marker::Send + 'static, + stderr_func: impl Fn(Lines>) -> Vec + std::marker::Send + 'static, +) -> CmdOutput { + // https://stackoverflow.com/a/72831067/16432246 + let start = Instant::now(); + let mut child = command + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .unwrap(); + + let child_stdout = child.stdout.take().unwrap(); + let child_stderr = child.stderr.take().unwrap(); + + let stdout_lines = BufReader::new(child_stdout).lines(); + let stderr_lines = BufReader::new(child_stderr).lines(); + + let stdout_thread = thread::spawn(move || stdout_func(stdout_lines)); + let stderr_thread = thread::spawn(move || stderr_func(stderr_lines)); + + let mut lines = stdout_thread.join().unwrap(); + let mut lines_printed_to_stderr = stderr_thread.join().unwrap(); + lines.append(&mut lines_printed_to_stderr); + lines.sort(); + + let status = child.wait().unwrap().code(); + let end = Instant::now(); + + return CmdOutput { + lines: Some(lines), + status_code: status, + start_time: start, + end_time: end, + duration: end.duration_since(start), + }; +} diff --git a/src/tests.rs b/src/tests.rs index d91aa69..30f87d4 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -88,19 +88,19 @@ fn shuffle_vec(vec: &mut [T]) { } #[test] -fn test_run_with_funcs() { +fn test_run_funcs() { let _ = thread::spawn(|| { - let _ = run_with_funcs( + let _ = run_funcs( Command::new("bash") .arg("-c") - .arg("echo hi; sleep 0.5; >&2 echo hello"), + .arg("echo hi; >&2 echo hello"), { |stdout_lines| { sleep(Duration::from_secs(1)); for _ in stdout_lines { Command::new("bash") .arg("-c") - .arg("echo stdout >> ./tmp") + .arg("echo stdout >> ./tmp-run_runcs") .output() .unwrap(); } @@ -112,7 +112,7 @@ fn test_run_with_funcs() { for _ in stderr_lines { Command::new("bash") .arg("-c") - .arg("echo stderr >> ./tmp") + .arg("echo stderr >> ./tmp-run_runcs") .output() .unwrap(); } @@ -121,7 +121,7 @@ fn test_run_with_funcs() { ); }); sleep(Duration::from_secs(2)); - let f = File::open("./tmp").unwrap(); + let f = File::open("./tmp-run_runcs").unwrap(); let mut buf: [u8; 14] = [0u8; 14]; f.read_at(&mut buf, 0).unwrap(); assert_eq!(buf, [115, 116, 100, 111, 117, 116, 10, 0, 0, 0, 0, 0, 0, 0]); @@ -133,5 +133,58 @@ fn test_run_with_funcs() { [115, 116, 100, 111, 117, 116, 10, 115, 116, 100, 101, 114, 114, 10] ); - remove_file("./tmp").unwrap(); + remove_file("./tmp-run_runcs").unwrap(); +} + +#[test] +fn test_run_funcs_with_lines() { + let _ = thread::spawn(|| { + let _ = run_funcs_with_lines( + Command::new("bash") + .arg("-c") + .arg("echo hi; >&2 echo hello"), + { + |stdout_lines| { + sleep(Duration::from_secs(1)); + for line in stdout_lines { + assert_eq!(line.unwrap(), "hi"); + Command::new("bash") + .arg("-c") + .arg("echo stdout >> ./tmp-run_runcs_with_lines") + .output() + .unwrap(); + } + return Vec::new(); + } + }, + { + |stderr_lines| { + sleep(Duration::from_secs(3)); + for line in stderr_lines { + assert_eq!(line.unwrap(), "hello"); + Command::new("bash") + .arg("-c") + .arg("echo stderr >> ./tmp-run_runcs_with_lines") + .output() + .unwrap(); + } + return Vec::new(); + } + }, + ); + }); + sleep(Duration::from_secs(2)); + let f = File::open("./tmp-run_runcs_with_lines").unwrap(); + let mut buf: [u8; 14] = [0u8; 14]; + f.read_at(&mut buf, 0).unwrap(); + assert_eq!(buf, [115, 116, 100, 111, 117, 116, 10, 0, 0, 0, 0, 0, 0, 0]); + + sleep(Duration::from_secs(2)); + f.read_at(&mut buf, 0).unwrap(); + assert_eq!( + buf, + [115, 116, 100, 111, 117, 116, 10, 115, 116, 100, 101, 114, 114, 10] + ); + + remove_file("./tmp-run_runcs_with_lines").unwrap(); }