mirror of https://github.com/chipsenkbeil/distant
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
104 lines
3.2 KiB
Rust
104 lines
3.2 KiB
Rust
1 year ago
|
use std::io::{BufRead, BufReader, Read};
|
||
|
use std::sync::mpsc;
|
||
|
use std::time::{Duration, Instant};
|
||
|
use std::{io, thread};
|
||
2 years ago
|
|
||
|
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(_) => {
|
||
|
// Consume the line and create an empty line to
|
||
|
// be filled in next time
|
||
|
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 }
|
||
|
}
|
||
|
|
||
|
/// Tries to read the next line if available
|
||
|
pub fn try_read_line(&mut self) -> Option<String> {
|
||
|
self.rx.try_recv().ok()
|
||
|
}
|
||
|
|
||
|
/// Reads the next line, waiting for at minimum "timeout"
|
||
|
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
|
||
|
}
|
||
|
|
||
|
/// Reads the next line, waiting for at minimum "timeout" before panicking
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
/// Reads the next line, waiting for at minimum default timeout before panicking
|
||
|
#[allow(dead_code)]
|
||
|
pub fn read_line_default_timeout(&mut self) -> String {
|
||
|
self.read_line_timeout(Self::default_timeout())
|
||
|
}
|
||
|
|
||
|
/// Creates a new duration representing a default timeout for the threaded reader
|
||
|
pub fn default_timeout() -> Duration {
|
||
|
Duration::from_millis(250)
|
||
|
}
|
||
|
|
||
|
/// Waits for reader to shut down, returning the result
|
||
|
#[allow(dead_code)]
|
||
|
pub fn wait(self) -> io::Result<()> {
|
||
|
match self.handle.join() {
|
||
|
Ok(x) => x,
|
||
|
Err(x) => std::panic::resume_unwind(x),
|
||
|
}
|
||
|
}
|
||
|
}
|