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
|
#![allow(clippy::trivial_regex)]
use futures::stream::Stream as _;
use std::io::Write as _;
use tokio::io::AsyncRead as _;
mod input;
struct Interhack {
process: tokio_pty_process_stream::Process<input::buf::Stdin>,
stdin: input::evented_stdin::Stdin,
read_buf: [u8; 4096],
raw_screen: Option<crossterm::RawScreen>,
}
impl Interhack {
fn new() -> Self {
Self {
process: tokio_pty_process_stream::Process::new(
"nethack",
&[],
input::buf::Stdin::new(),
),
stdin: input::evented_stdin::Stdin::new(),
read_buf: [0; 4096],
raw_screen: None,
}
}
fn filter_input(&self, buf: Vec<u8>) -> Vec<u8> {
lazy_static::lazy_static! {
static ref RE: regex::bytes::Regex = regex::bytes::Regex::new(
"\x05"
).unwrap();
}
if let Some(m) = RE.find(&buf) {
let mut new: Vec<u8> = vec![];
new.extend(buf[..m.start()].iter());
new.extend(b"E- Elbereth\n");
new.extend(buf[m.end()..].iter());
new
} else {
buf
}
}
fn filter_output(&self, buf: Vec<u8>) -> Vec<u8> {
lazy_static::lazy_static! {
static ref RE: regex::bytes::Regex = regex::bytes::Regex::new(
r"Elbereth"
).unwrap();
}
if let Some(m) = RE.find(&buf) {
let mut new: Vec<u8> = vec![];
new.extend(buf[..m.start()].iter());
new.extend(b"\x1b[35m");
new.extend(buf[m.start()..m.end()].iter());
new.extend(b"\x1b[m");
new.extend(buf[m.end()..].iter());
new
} else {
buf
}
}
}
#[allow(clippy::type_complexity)]
impl Interhack {
const POLL_FNS:
&'static [&'static dyn for<'a> Fn(
&'a mut Self,
)
-> component_future::Poll<(), ()>] =
&[&Self::poll_input, &Self::poll_process, &Self::poll_screen];
fn poll_input(&mut self) -> component_future::Poll<(), ()> {
let n = component_future::try_ready!(self
.stdin
.poll_read(&mut self.read_buf)
.map_err(|e| panic!(e)));
let input = self.filter_input(self.read_buf[..n].to_vec());
self.process.input().send(&input);
Ok(component_future::Async::DidWork)
}
fn poll_process(&mut self) -> component_future::Poll<(), ()> {
let event = component_future::try_ready!(self
.process
.poll()
.map_err(|e| panic!(e)));
match event {
Some(tokio_pty_process_stream::Event::Output { data }) => {
let output = self.filter_output(data);
let stdout = std::io::stdout();
let mut stdout = stdout.lock();
stdout.write_all(&output).unwrap();
stdout.flush().unwrap();
}
None => return Ok(component_future::Async::Ready(())),
_ => {}
}
Ok(component_future::Async::DidWork)
}
fn poll_screen(&mut self) -> component_future::Poll<(), ()> {
if self.raw_screen.is_none() {
self.raw_screen =
Some(crossterm::RawScreen::into_raw_mode().unwrap());
Ok(component_future::Async::DidWork)
} else {
Ok(component_future::Async::NothingToDo)
}
}
}
impl futures::future::Future for Interhack {
type Item = ();
type Error = ();
fn poll(&mut self) -> futures::Poll<Self::Item, Self::Error> {
component_future::poll_future(self, Self::POLL_FNS)
}
}
fn main() {
tokio::run(futures::future::lazy(Interhack::new));
}
|