-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathoracle.rs
90 lines (76 loc) · 2.48 KB
/
oracle.rs
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
use std::process::{ Command, Stdio, Child, ChildStdin, ChildStdout };
use std::io::{ Write, BufReader, BufRead, BufWriter };
use std::time::Instant;
use std::thread;
use snafu::{ Snafu, ResultExt };
use base64;
use log::trace;
type Result<T, E = Error> = std::result::Result<T, E>;
#[derive(Debug, Snafu)]
pub enum Error {
#[snafu(display("oracle io returned an error: {}", source))]
BrokenIO { source: std::io::Error },
#[snafu(display("oracle is being a weirdo: {}", reason))]
BrokenLogic { reason: String }
}
pub struct CmdOracleCtx {
cmd: String,
args: Vec<String>
}
impl CmdOracleCtx {
pub fn new(cmd: String, args: Vec<String>) -> Self {
CmdOracleCtx { cmd, args }
}
pub fn spawn(&self) -> Result<CmdOracle> {
CmdOracle::new(self)
}
}
pub struct CmdOracle{
child: Child,
writer: BufWriter<ChildStdin>,
reader: BufReader<ChildStdout>
}
impl Drop for CmdOracle {
fn drop(&mut self) {
// TODO hmmmmmm
// kill forcefully, ignore errors
self.child.kill().unwrap();
}
}
impl CmdOracle {
fn new(ctx: &CmdOracleCtx) -> Result<Self> {
let mut child = Command::new(&ctx.cmd)
.args(&ctx.args)
.stdout(Stdio::piped())
.stdin(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.context(BrokenIO)?;
let err = child.stderr.take().unwrap();
thread::spawn(|| {
BufReader::new(err).lines()
.for_each(|l| log::error!("stderr: {}", l.unwrap()));
log::debug!("stderr thread exit");
});
let writer = BufWriter::new(child.stdin.take().unwrap());
let reader = BufReader::new(child.stdout.take().unwrap());
Ok(CmdOracle { child, writer, reader })
}
pub fn request(&mut self, payload: &[u8]) -> Result<bool> {
let now = Instant::now();
self.writer.write_all(base64::encode(payload).as_bytes()).context(BrokenIO)?;
self.writer.write(&['\n' as u8]).context(BrokenIO)?;
self.writer.flush().context(BrokenIO)?;
// 'y' or 'n' with a newline
let mut line = String::new();
self.reader.read_line(&mut line)
.context(BrokenIO)?;
trace!("oracle took {:?}", now.elapsed());
match line.trim() {
"yes" => Ok(true),
"no" => Ok(false),
x => Err(Error::BrokenLogic { reason:
format!("invalid choice: {:?}. choices are: yes/no", x)})
}
}
}