1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#![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 struct EvalError(EvalCodeError);
impl EvalError {
pub fn build_failed(&self) -> bool {
if let EvalCodeError::Build(_) = self.0 {true} else {false}
}
}
impl Error for EvalError {
fn cause(&self) -> Option<&Error> {
match self.0 {
EvalCodeError::Build(_) => None,
EvalCodeError::FileIo(ref e) => Some(e),
EvalCodeError::RunProgram(_) => None,
EvalCodeError::StartBuild(ref e) => Some(e),
EvalCodeError::StartProgram(ref e) => Some(e),
}
}
fn description(&self) -> &str {
match self.0 {
EvalCodeError::Build(_) => "Build failed",
EvalCodeError::FileIo(_) => "File IO error",
EvalCodeError::RunProgram(_) => "Program didn't terminate \
successfully",
EvalCodeError::StartBuild(_) => "Failed to start build",
EvalCodeError::StartProgram(_) => "Failed to start program",
}
}
}
impl Display for EvalError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.description())?;
let s = match self.0 {
EvalCodeError::Build(ref s) => s,
EvalCodeError::RunProgram(ref s) => s,
_ => return Ok(()),
};
write!(f, "\n{}", s)
}
}
pub fn eval(code: &str) -> Result<String, EvalError> {
eval_code(code).map_err(EvalError)
}
#[derive(Debug)]
enum EvalCodeError {
Build(String),
FileIo(io::Error),
RunProgram(String),
StartBuild(io::Error),
StartProgram(io::Error),
}
fn eval_code(code: &str) -> Result<String, EvalCodeError> {
let temp = TempDir::new("").map_err(EvalCodeError::FileIo)?;
let code_path = temp.path().join("main.rs");
write_source_file(&code_path, code).map_err(EvalCodeError::FileIo)?;
let out_path = temp.path().join("main");
let out = Command::new("rustc")
.arg("-o")
.arg(&out_path)
.arg(&code_path)
.output()
.map_err(EvalCodeError::StartBuild)?;
if !out.status.success() {
return Err(EvalCodeError::Build(
String::from_utf8_lossy(&out.stderr).into_owned()))
}
let out = Command::new(&out_path).output()
.map_err(EvalCodeError::StartProgram)?;
if out.status.success() {
Ok(String::from_utf8_lossy(&out.stdout).into_owned())
} else {
Err(EvalCodeError::RunProgram(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()?;
Ok(())
}