bf-lib 0.2.1

A library to run brainfuck code
Documentation
use crate::{bf::*, check_brackets, Error, RuntimeError};
use rand::{distributions::Alphanumeric, Rng};
use std::{env, fs, path::PathBuf};
use subprocess::{Exec, ExitStatus, Redirection};

pub fn run(
    program: &str,
    input: Option<String>,
    time: Option<std::time::Duration>,
    tmp_path: Option<PathBuf>,
) -> Result<String, Error> {
    let code = translate(program, input)?;
    let name = "bf".to_owned()
        + &rand::thread_rng()
            .sample_iter(Alphanumeric)
            .take(14)
            .collect::<String>();
    let basepath = if let Some(s) = tmp_path {
        s
    } else {
        env::current_dir().unwrap()
    };
    {
        let mut source = basepath.clone();
        source.push(name.clone() + ".rs");
        fs::write(source, code).unwrap();
    }
    let rustc = Exec::cmd("rustc")
        .arg("-Copt-level=3")
        .arg(name.clone() + ".rs")
        .stderr(Redirection::Pipe)
        .capture()
        .unwrap();
    if !rustc.success() {
        return Err(Error::Compile(rustc.stderr_str()));
    }
    let mut exe = basepath;
    if cfg!(windows) {
        exe.push(format!(r".\{}.exe", name))
    } else {
        exe.push(format!("./{}", name))
    };

    let result = if let Some(t) = time {
        let mut p = Exec::cmd(exe)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .popen()
            .unwrap();
        match p.wait_timeout(t) {
            Ok(Some(ExitStatus::Exited(c))) => match c {
                0 => Ok(p
                    .communicate_start(None)
                    .read()
                    .unwrap()
                    .0
                    .unwrap_or(vec![])
                    .into_iter()
                    .map(|c| c as char)
                    .collect::<String>()
                    .trim()
                    .to_owned()),
                10 => Err(Error::Runtime(RuntimeError::InputTooShort)),
                _ => Err(Error::Runtime(RuntimeError::OutOfMemoryBounds)),
            },
            Ok(Some(_)) => Err(Error::Runtime(RuntimeError::Signal)),
            Ok(None) => {
                p.terminate().unwrap();
                Err(Error::Timeout)
            }
            Err(e) => Err(Error::Subprocess(e)),
        }
    } else {
        let p = Exec::cmd(exe)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()
            .unwrap();
        if let ExitStatus::Exited(c) = p.exit_status {
            match c {
                0 => Ok(p.stdout_str().trim().to_owned()),
                10 => Err(Error::Runtime(RuntimeError::InputTooShort)),
                _ => Err(Error::Runtime(RuntimeError::OutOfMemoryBounds)),
            }
        } else {
            Err(Error::Runtime(RuntimeError::Signal))
        }
    };
    cleanup(&name);
    result
}

pub fn translate(program: &str, input: Option<String>) -> Result<String, Error> {
    check_brackets(program)?;
    let i1 = program.to_inst()?;
    Ok(to_rust(i1, input))
}

fn cleanup(name: &str) {
    fs::remove_file(format!("{}.rs", name)).unwrap();
    fs::remove_file(if cfg!(windows) {
        format!(".\\{}.exe", name)
    } else {
        format!("./{}", name)
    })
    .unwrap();
}

fn to_rust(inst: Vec<Instruction>, input: Option<String>) -> String {
    const START: &str = "use std::num::Wrapping;
fn main() {
let mut _m = [Wrapping(0u8); 30000];
let (mut _p, mut _b) = (0usize, 0usize);
let mut _o = String::new();\n";
    const END: &str = "println!(\"{}\", _o);}";
    let mut code = if let Some(s) = input {
        format!("let _i = \"{}\";\n", s)
    } else {
        String::new()
    };
    for i in inst {
        match i {
            Instruction::Right(x) => code.push_str(&format!("_p += {};\n", x)),
            Instruction::Left(x) => code.push_str(&format!("_p -= {};\n", x)),
            Instruction::Add(x) => code.push_str(&format!("_m[_p] += Wrapping({});\n", x)),
            Instruction::Sub(x) => code.push_str(&format!("_m[_p] -= Wrapping({});\n", x)),
            Instruction::Print => code.push_str("_o.push(_m[_p].0 as char)\n;"),
            Instruction::Read => code.push_str(
                "_m[_p] = { _b += 1; if let Some(c) = _i.as_bytes().get(_b-1) { Wrapping(*c)
} else { std::process::exit(10) }};\n",
            ),
            Instruction::LoopStart => code.push_str("while _m[_p].0 != 0 {\n"),
            Instruction::LoopEnd => code.push_str("}\n"),
        }
    }
    format!("{}{}{}", START, code, END)
}