recl 0.2.1

A simple program to record CLI with.
use crate::log::{log_from_bytes, Log};
use std::io::{stderr, stdout, BufReader, Read};
use std::process::{Command, Stdio};
use std::sync::Mutex;
use std::thread::spawn;
use std::time::Instant;

pub fn record(cmd: &[String]) -> Result<Log, String> {
    match cmd.get(0) {
        Some(exe) => match Command::new(exe)
            .args(&cmd[1..])
            .stdout(Stdio::piped())
            .stderr(Stdio::piped())
            .spawn()
        {
            Ok(child) => {
                let out = Mutex::new(child.stdout);
                let err = Mutex::new(child.stderr);

                let start = Instant::now();

                let mut handles = Vec::new();

                handles.push(spawn(move || {
                    let mut out = out.lock().unwrap();
                    if let Some(mut bytes) = out
                        .as_mut()
                        .map(|s| BufReader::new(s as &mut dyn Read).bytes())
                    {
                        Some(log_from_bytes(&mut bytes, &start, 1, &mut stdout()))
                    } else {
                        None
                    }
                }));

                handles.push(spawn(move || {
                    let mut err = err.lock().unwrap();
                    if let Some(mut bytes) = err
                        .as_mut()
                        .map(|s| BufReader::new(s as &mut dyn Read).bytes())
                    {
                        Some(log_from_bytes(&mut bytes, &start, 2, &mut stderr()))
                    } else {
                        None
                    }
                }));

                let mut log: Log = Vec::new();
                for handle in handles {
                    log.extend(handle.join().unwrap().unwrap());
                }
                log.sort_unstable_by_key(|entry| entry.timestamp);

                Ok(log)
            }
            Err(e) => Err(format!("{}", e)),
        },
        None => Err("No command given".to_string()),
    }
}