feat: basic runner that can capture stdout

This commit is contained in:
Gabriele Musco 2023-06-01 18:32:22 +02:00
parent 5116f7d794
commit 9880b8e4e7
2 changed files with 123 additions and 0 deletions

View file

@ -1,4 +1,5 @@
pub mod profile;
pub mod runner;
fn main() {
println!("Hello, world!");

122
src/runner.rs Normal file
View file

@ -0,0 +1,122 @@
use std::{
collections::HashMap,
io::{BufRead, BufReader},
process::{Child, Command, Stdio},
};
pub struct Runner {
environment: HashMap<String, String>,
command: String,
args: Vec<String>,
stdout: Vec<String>,
stderr: Vec<String>,
process: Option<Child>,
}
#[derive(PartialEq, Eq, Debug)]
pub enum RunnerStatus {
Running,
Stopped,
}
impl Runner {
pub fn new(environment: HashMap<String, String>, command: String, args: Vec<String>) -> Runner {
Runner {
environment,
command,
args,
stdout: Vec::new(),
stderr: Vec::new(),
process: None,
}
}
pub fn start(&mut self) {
self.process = Some(
Command::new(&self.command)
.args(&self.args)
.envs(self.environment.clone())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.expect("Failed to execute runner"),
);
}
pub fn terminate(&mut self) {
match self.process.as_mut() {
None => {
println!("Runner already stopped")
}
Some(proc) => {
proc.kill().expect("Failed to kill process");
proc.wait().expect("Failed to wait for process");
self.process = None
}
}
}
pub fn status(&mut self) -> RunnerStatus {
match self.process.as_mut() {
None => RunnerStatus::Stopped,
Some(proc) => match proc.try_wait() {
Ok(_) => RunnerStatus::Stopped,
Err(_) => RunnerStatus::Running,
},
}
}
pub fn flush_out(&mut self) {
match self.process.as_mut() {
None => (),
Some(proc) => {
let out = proc.stdout.take().expect("Unable to read stout");
let mut reader = BufReader::new(out);
let mut bytes_read = 1;
let mut buf = "".to_string();
while bytes_read != 0 {
bytes_read = match reader.read_line(&mut buf) {
Ok(bytes) => bytes,
Err(_) => 0,
};
self.stdout.push(buf.clone());
buf.clear();
}
let err = proc.stderr.take().expect("Unable to read stderr");
let mut err_reader = BufReader::new(err);
bytes_read = 1;
while bytes_read != 0 {
bytes_read = match err_reader.read_line(&mut buf) {
Ok(bytes) => bytes,
Err(_) => 0,
};
self.stderr.push(buf.clone());
buf.clear();
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::{Runner, RunnerStatus};
use core::time;
use std::{collections::HashMap, thread::sleep};
#[test]
fn can_run_command_and_read_env() {
let mut env = HashMap::new();
env.insert("REX2TEST".to_string(), "Lorem ipsum dolor".to_string());
let mut runner = Runner::new(env, "bash".into(), vec!["-c".into(), "echo \"REX2TEST: $REX2TEST\"".into()]);
runner.start();
while runner.status() == RunnerStatus::Running {
sleep(time::Duration::from_millis(10));
}
runner.flush_out();
assert_eq!(runner.status(), RunnerStatus::Stopped);
assert_eq!(runner.stdout.len(), 2);
assert_eq!(runner.stdout.get(0).unwrap(), "REX2TEST: Lorem ipsum dolor\n");
}
}