use std::io::{IsTerminal, Read};
pub trait InputStream: Read {
fn is_tty(&self) -> bool {
false
}
}
pub struct StdinStream(std::io::Stdin);
impl StdinStream {
pub fn new() -> Self {
Self(std::io::stdin())
}
}
impl Default for StdinStream {
fn default() -> Self {
Self::new()
}
}
impl Read for StdinStream {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
self.0.read(buf)
}
}
impl InputStream for StdinStream {
fn is_tty(&self) -> bool {
self.0.is_terminal()
}
}
pub struct ReaderInput<R: Read> {
reader: R,
tty: bool,
}
impl<R: Read> ReaderInput<R> {
pub fn new(reader: R, tty: bool) -> Self {
Self { reader, tty }
}
}
impl<R: Read> Read for ReaderInput<R> {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
self.reader.read(buf)
}
}
impl<R: Read> InputStream for ReaderInput<R> {
fn is_tty(&self) -> bool {
self.tty
}
}
pub(crate) fn read_line(stdin: &mut dyn InputStream) -> std::io::Result<Option<String>> {
let mut buf = Vec::new();
let mut byte = [0u8; 1];
loop {
match stdin.read(&mut byte)? {
0 => {
if buf.is_empty() {
return Ok(None);
}
break;
}
_ => {
if byte[0] == b'\n' {
break;
}
buf.push(byte[0]);
}
}
}
let mut line = String::from_utf8_lossy(&buf).into_owned();
if line.ends_with('\r') {
line.pop();
}
Ok(Some(line))
}
pub(crate) fn drain(stdin: &mut dyn InputStream) {
let mut sink = [0u8; 8192];
loop {
match stdin.read(&mut sink) {
Ok(0) | Err(_) => break,
Ok(_) => {}
}
}
}