use ::std::io::BufRead;
use ::std::io::{stdin, BufReader, Stdin};
use ::std::process::exit;
use ::async_std::prelude::FutureExt as AltExt;
use ::async_trait::async_trait;
use ::futures::{AsyncReadExt, FutureExt};
use ::log::debug;
use crate::common::async_gate::AsyncGate;
#[async_trait]
pub trait LineReader: Send {
async fn read_line(&mut self) -> Option<&str>;
async fn collect_all(&mut self) -> Vec<String> {
let mut all = vec![];
while let Some(line) = self.read_line().await {
all.push(line.to_owned())
}
all
}
}
#[derive(Debug)]
pub struct StdinReader {
reader: BufReader<Stdin>,
buffer: String,
}
impl StdinReader {
pub fn new() -> Self {
StdinReader {
reader: BufReader::new(stdin()),
buffer: String::with_capacity(256),
}
}
}
impl Default for StdinReader {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl LineReader for StdinReader {
async fn read_line(&mut self) -> Option<&str> {
self.buffer.clear();
let read_len = self.reader.read_line(&mut self.buffer).unwrap();
while self.buffer.ends_with('\n') || self.buffer.ends_with('\r') {
self.buffer.pop();
}
if read_len == 0 {
return None;
}
Some(&self.buffer)
}
}
#[derive(Debug)]
pub struct RejectStdin {
gate: AsyncGate,
}
#[derive(Debug, PartialEq)]
enum StdinWaitResult {
Data,
Completed,
}
impl RejectStdin {
pub fn new() -> Self {
let gate = AsyncGate::new();
let gate_clone = gate.clone();
#[allow(clippy::redundant_closure_call)] async_std::task::spawn((async move || {
debug!("starting monitor to reject stdin input");
let res = async_std::io::stdin()
.read(&mut [0])
.map(|_| StdinWaitResult::Data)
.race(gate_clone.wait().map(|_| StdinWaitResult::Completed))
.await;
if res == StdinWaitResult::Data {
eprintln!("received data on stdin but did not expect any");
exit(1);
}
debug!("finished stdin rejection monitor because the reader was dropped");
})());
RejectStdin { gate }
}
}
impl Default for RejectStdin {
fn default() -> Self {
RejectStdin::new()
}
}
impl Drop for RejectStdin {
fn drop(&mut self) {
self.gate.open(true);
}
}
#[async_trait]
impl LineReader for RejectStdin {
async fn read_line(&mut self) -> Option<&str> {
todo!() }
}
#[derive(Debug)]
pub struct VecReader {
lines: Vec<String>,
current: String,
}
impl VecReader {
pub fn new<S: Into<String>>(mut lines: Vec<S>) -> Self {
lines.reverse();
VecReader {
lines: lines.into_iter().map(|line| line.into()).collect(),
current: "".to_string(),
}
}
}
#[async_trait]
impl LineReader for VecReader {
async fn read_line(&mut self) -> Option<&str> {
self.current = self.lines.pop()?;
Some(&self.current)
}
}