use std::io::{BufRead, BufReader, Read};
use std::sync::mpsc;
use std::time::{Duration, Instant};
use std::{io, thread};
pub struct ThreadedReader {
#[allow(dead_code)]
handle: thread::JoinHandle<io::Result<()>>,
rx: mpsc::Receiver<String>,
}
impl ThreadedReader {
pub fn new<R>(reader: R) -> Self
where
R: Read + Send + 'static,
{
let (tx, rx) = mpsc::channel();
let handle = thread::spawn(move || {
let mut reader = BufReader::new(reader);
let mut line = String::new();
loop {
match reader.read_line(&mut line) {
Ok(0) => break Ok(()),
Ok(_) => {
let line2 = line;
line = String::new();
if let Err(line) = tx.send(line2) {
return Err(io::Error::new(
io::ErrorKind::Other,
format!(
"Failed to pass along line because channel closed! Line: '{}'",
line.0
),
));
}
}
Err(x) => return Err(x),
}
}
});
Self { handle, rx }
}
pub fn try_read_line(&mut self) -> Option<String> {
self.rx.try_recv().ok()
}
pub fn try_read_line_timeout(&mut self, timeout: Duration) -> Option<String> {
let start_time = Instant::now();
let mut checked_at_least_once = false;
while !checked_at_least_once || start_time.elapsed() < timeout {
if let Some(line) = self.try_read_line() {
return Some(line);
}
checked_at_least_once = true;
}
None
}
pub fn read_line_timeout(&mut self, timeout: Duration) -> String {
let start_time = Instant::now();
let mut checked_at_least_once = false;
while !checked_at_least_once || start_time.elapsed() < timeout {
if let Some(line) = self.try_read_line() {
return line;
}
checked_at_least_once = true;
}
panic!("Reached timeout of {:?}", timeout);
}
#[allow(dead_code)]
pub fn read_line_default_timeout(&mut self) -> String {
self.read_line_timeout(Self::default_timeout())
}
pub fn default_timeout() -> Duration {
Duration::from_millis(250)
}
#[allow(dead_code)]
pub fn wait(self) -> io::Result<()> {
match self.handle.join() {
Ok(x) => x,
Err(x) => std::panic::resume_unwind(x),
}
}
}