Skip to content

Instantly share code, notes, and snippets.

@andreivasiliu
Last active March 10, 2019 07:07
Show Gist options
  • Save andreivasiliu/a4a2a6007d1659799b1f8057e264a61d to your computer and use it in GitHub Desktop.
Save andreivasiliu/a4a2a6007d1659799b1f8057e264a61d to your computer and use it in GitHub Desktop.
minitrack
extern crate crossbeam;
use std::io::{stdin, BufRead, BufReader};
use std::process::{Command, Stdio, ExitStatus};
use crossbeam::channel::{Sender, Receiver, unbounded};
#[derive(Debug)]
enum ProcessEvent {
ProcessStarted(u32),
Stdout(String),
Stderr(String),
ProcessEnded(u32, ExitStatus),
}
fn run_process(line: String, output: Sender<ProcessEvent>) {
let mut child = Command::new("cmd")
.arg("/C")
.arg(line)
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.expect("cmd doesn't exist!");
let pid = child.id();
output.send(ProcessEvent::ProcessStarted(pid));
crossbeam::thread::scope(|scope| {
let child_stdout = child.stdout.as_mut().unwrap();
let child_stderr = child.stderr.as_mut().unwrap();
let stdout_thread = scope.spawn(|| {
let child_stdout = BufReader::new(child_stdout);
for child_line in child_stdout.lines() {
if let Ok(child_line) = child_line {
output.send(ProcessEvent::Stdout(child_line))
}
}
});
let stderr_thread = scope.spawn(|| {
let child_stderr = BufReader::new(child_stderr);
for child_line in child_stderr.lines() {
if let Ok(child_line) = child_line {
output.send(ProcessEvent::Stderr(child_line));
}
}
});
stdout_thread.join().unwrap();
stderr_thread.join().unwrap();
});
let status = child.wait().expect("Oops");
output.send(ProcessEvent::ProcessEnded(pid, status));
}
fn process_starter(input: Receiver<String>, output: Sender<ProcessEvent>) {
crossbeam::thread::scope(|scope| {
for command in input {
scope.spawn(|| {
run_process(command, output.clone());
});
}
});
}
fn command_reader<R: BufRead>(source: R, output: Sender<String>) {
for line in source.lines() {
if let Ok(line) = line {
output.send(line);
}
}
}
fn main() {
println!("Now starting.");
let (command_sender, command_receiver) = unbounded();
let (output_sender, output_receiver) = unbounded();
let reader_thread = std::thread::spawn(move || {
command_reader(stdin().lock(), command_sender);
});
let starter_thread = std::thread::spawn(move || {
process_starter(command_receiver, output_sender);
});
for event in output_receiver {
println!("Event: {:?}", event);
}
starter_thread.join().unwrap();
reader_thread.join().unwrap();
}

Sample output (I typed "dir" and "blah" by hand):

Now starting.
dir
Event: ProcessStarted(106712)
Event: Stdout(" Volume in drive C has no label.")
Event: Stdout(" Volume Serial Number is DEDD-7699")
Event: Stdout("")
Event: Stdout(" Directory of C:\\Users\\Andrei\\Desktop\\rust-minitrack\\src")
Event: Stdout("")
Event: Stdout("03/09/2019  11:13 PM    <DIR>          .")
Event: Stdout("03/09/2019  11:13 PM    <DIR>          ..")
Event: Stdout("03/09/2019  11:13 PM             2,682 main.rs")
Event: Stdout("               1 File(s)          2,682 bytes")
Event: Stdout("               2 Dir(s)   7,278,260,224 bytes free")
Event: ProcessEnded(106712, ExitStatus(ExitStatus(0)))
blah
Event: ProcessStarted(106836)
Event: Stderr("\'blah\' is not recognized as an internal or external command,")
Event: Stderr("operable program or batch file.")
Event: ProcessEnded(106836, ExitStatus(ExitStatus(1)))
[package]
name = "rust-minitrack"
version = "0.1.0"
edition = "2018"
[dependencies]
crossbeam = "0.4"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment