batch_run 1.2.0

Batch runner for arbitrary Rust files within current project
Documentation
use crate::result::{BatchError, BatchResult};
use crate::result::{EntryError, EntryResult};
use std::{
    env::{consts::EXE_EXTENSION, var_os},
    path::{Path, PathBuf},
    process::{Command, Output},
};

use crate::binary::BinaryBuilder;
use crate::rustflags;

thread_local! {
    static TARGET_BIN: PathBuf = {
        let mut tmp: PathBuf = [".", "target", "batch"].iter().collect();
        // TODO configurable?
        std::fs::create_dir_all(&tmp).expect("Unable to create batch executable in target directory; check your access rights");
        tmp.push(&format!("{:x}", rand::random::<u64>()));
        tmp.with_extension(EXE_EXTENSION)
    };
}

include!(concat!(env!("OUT_DIR"), "/info.rs"));

fn raw_cargo() -> Command {
    Command::new(option_env!("CARGO").unwrap_or("cargo"))
}

fn rustc() -> Command {
    let mut cmd = Command::new(info::rustc());
    cmd.current_dir(var_os("CARGO_MANIFEST_DIR").unwrap());
    TARGET_BIN.with(|bin| cmd.args(&["-o", bin.to_str().expect("Non-UTF-8 symbols in path")]));
    cmd
}

pub fn capture_test_command(test_name: &str) -> BatchResult<String> {
    let mut cmd = raw_cargo();
    cmd.current_dir(var_os("CARGO_MANIFEST_DIR").unwrap());
    rustflags::set_env(&mut cmd);
    cmd.arg("test");
    if info::opt_level() == "release" {
        cmd.arg("--release");
    };
    cmd.arg(test_name)
        .arg("--verbose")
        .output()
        .map_err(BatchError::Cargo)
        .map_err(Into::into)
        // .map(|out| { println!("Cargo output: \"{}\"", String::from_utf8(out.clone().stderr).unwrap()); out })
        .map(|out| extract_test_command(test_name, out))
        .map(trim_test_command)
}

fn extract_test_command(test_name: &str, out: Output) -> String {
    String::from_utf8(out.stderr)
        .expect("Cargo produced non-UTF-8 output")
        .lines()
        // .map(|out| { println!("Cargo output: \"{}\"", out); out })
        .filter(|line| line.contains(&format!("tests/{}.rs", test_name)))
        .last()
        .expect("No running command in cargo output")
        .to_owned()
}

fn trim_test_command(line: String) -> String {
    line.trim_start_matches("Running")
        .trim()
        .trim_matches('`')
        .to_owned()
}

pub fn build_entry(builder: &BinaryBuilder, main: &Path, run: bool) -> EntryResult<Output> {
    let mut cmd = rustc();
    builder.args_to_command(&mut cmd, main);
    cmd.arg(if run {
        "--emit=link"
    } else {
        "--emit=metadata"
    });
    cmd.output().map_err(EntryError::Rustc).map_err(Into::into)
}

pub fn run_entry() -> EntryResult<Output> {
    TARGET_BIN
        .with(|bin| Command::new(bin))
        .output()
        .map_err(EntryError::RunFailed)
        .map_err(Into::into)
}