#![deny(warnings)]
#![deny(missing_docs)]
extern crate tempdir;
use std::error::Error;
use std::fmt::{Display, self};
use std::fs::File;
use std::io::{self, Write};
use std::path::Path;
use std::process::Command;
use tempdir::TempDir;
#[derive(Debug)]
pub enum EvalError {
Build(String),
Other(OtherFailure),
ProgReturnedError(String),
}
impl Error for EvalError {
fn cause(&self) -> Option<&Error> {
match *self {
EvalError::Other(ref e) => Some(&e.0),
_ => None,
}
}
fn description(&self) -> &str {
match *self {
EvalError::Build(_) => "Build failed",
EvalError::Other(_) => "Other error",
EvalError::ProgReturnedError(_) => "Program returned an error",
}
}
}
impl Display for EvalError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.description())?;
let s = match *self {
EvalError::Build(ref s) => s,
EvalError::ProgReturnedError(ref s) => s,
_ => return Ok(()),
};
write!(f, "\n{}", s)
}
}
#[derive(Debug)]
pub struct OtherFailure(OtherError);
#[derive(Debug)]
enum OtherError {
CreateTempDir(io::Error),
SpawnProg(io::Error),
SpawnRustc(io::Error),
WriteSrcFile(io::Error),
}
impl Error for OtherError {
fn cause(&self) -> Option<&Error> {
match *self {
OtherError::CreateTempDir(ref e) => Some(e),
OtherError::SpawnProg(ref e) => Some(e),
OtherError::SpawnRustc(ref e) => Some(e),
OtherError::WriteSrcFile(ref e) => Some(e),
}
}
fn description(&self) -> &str {
match *self {
OtherError::CreateTempDir(_) => "Failed to create temporary \
directory",
OtherError::SpawnProg(_) => "Failed to spawn program",
OtherError::SpawnRustc(_) => "Failed to spawn rustc",
OtherError::WriteSrcFile(_) => "Failed to write source file",
}
}
}
impl Display for OtherError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.description())
}
}
impl From<OtherError> for EvalError {
fn from(e: OtherError) -> EvalError {
EvalError::Other(OtherFailure(e))
}
}
pub fn eval(code: &str) -> Result<String, EvalError> {
let temp = TempDir::new("").map_err(OtherError::CreateTempDir)?;
let code_path = temp.path().join("main.rs");
write_source_file(&code_path, code).map_err(OtherError::WriteSrcFile)?;
let out_path = temp.path().join("main");
let out = Command::new("rustc")
.arg("-o")
.arg(&out_path)
.arg(&code_path)
.output()
.map_err(OtherError::SpawnRustc)?;
if !out.status.success() {
return Err(EvalError::Build(String::from_utf8_lossy(&out.stderr)
.into_owned()))
}
let out = Command::new(&out_path).output().map_err(OtherError::SpawnProg)?;
if out.status.success() {
Ok(String::from_utf8_lossy(&out.stdout).into_owned())
} else {
Err(EvalError::ProgReturnedError(String::from_utf8_lossy(&out.stderr)
.into_owned()))
}
}
fn write_source_file(path: &Path, code: &str) -> io::Result<()> {
let mut f = File::create(path)?;
write!(&mut f, r##"
fn main() {{
let expr = {{{}}};
print!("{{:?}}", expr);
}}
"##, code)?;
f.sync_all()
}