use std::io::{self, BufRead};
use std::{env, ffi, fs, iter, slice};
pub fn new() -> Diamond {
Diamond::default()
}
#[derive(Debug, Default)]
pub struct Diamond {
cur_file: Option<Reader>,
cur_arg: Option<ffi::OsString>,
args: Args,
}
impl Diamond {
pub fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> io::Result<usize> {
self.read_inner(|reader| reader.read_until(byte, buf))
}
pub fn read_line(&mut self, buf: &mut String) -> io::Result<usize> {
self.read_inner(|reader| reader.read_line(buf))
}
pub fn line_iter(mut self) -> impl Iterator<Item = io::Result<String>> {
iter::from_fn(move || {
let mut buf = String::new();
match self.read_line(&mut buf) {
Ok(0) => None,
Ok(_) => Some(Ok(buf)),
Err(e) => Some(Err(e)),
}
})
}
pub fn reader(self) -> impl BufRead {
struct SingleStreamReader(Diamond);
impl io::Read for SingleStreamReader {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let n = self.fill_buf()?.read(buf)?;
self.consume(n);
Ok(n)
}
}
impl BufRead for SingleStreamReader {
fn fill_buf(&mut self) -> io::Result<&[u8]> {
loop {
if let Some(reader) = &mut self.0.cur_file {
let ret = reader.as_buf_read_mut().fill_buf()?;
if !ret.is_empty() {
return Ok(unsafe { slice::from_raw_parts(ret.as_ptr(), ret.len()) });
}
}
if !self.0.prepare_next()? {
return Ok(&[]);
}
}
}
fn consume(&mut self, amount: usize) {
if let Some(reader) = &mut self.0.cur_file {
reader.as_buf_read_mut().consume(amount);
}
}
}
SingleStreamReader(self)
}
pub fn current_arg(&self) -> Option<&ffi::OsStr> {
self.cur_arg.as_deref()
}
fn read_inner(
&mut self,
mut f: impl FnMut(&mut dyn BufRead) -> io::Result<usize>,
) -> io::Result<usize> {
loop {
if let Some(reader) = &mut self.cur_file {
let ret = f(reader.as_buf_read_mut())?;
if ret != 0 {
return Ok(ret);
}
}
if !self.prepare_next()? {
return Ok(0);
}
}
}
fn prepare_next(&mut self) -> io::Result<bool> {
self.cur_file = None;
self.cur_arg = self.args.next();
if let Some(arg) = self.cur_arg.as_deref() {
self.cur_file = Some(Reader::open(arg)?);
Ok(true)
} else {
Ok(false)
}
}
}
#[derive(Debug, Default)]
struct Args(Option<iter::Fuse<env::ArgsOs>>);
impl Iterator for Args {
type Item = ffi::OsString;
fn next(&mut self) -> Option<Self::Item> {
if let Some(args) = &mut self.0 {
args.next()
} else {
let mut args = env::args_os().fuse();
args.next(); self.0.insert(args).next().or_else(|| Some("-".into()))
}
}
}
#[derive(Debug)]
#[non_exhaustive]
enum Reader {
Stdin(io::StdinLock<'static>),
File(io::BufReader<fs::File>),
}
impl Reader {
fn open(arg: &ffi::OsStr) -> io::Result<Self> {
if arg == "-" {
Ok(Self::Stdin(io::stdin().lock()))
} else {
let file = fs::File::open(arg)?;
Ok(Self::File(io::BufReader::new(file)))
}
}
fn as_buf_read_mut(&mut self) -> &mut dyn BufRead {
match self {
Self::Stdin(r) => r,
Self::File(r) => r,
}
}
}