use anyhow::{Context, Result};
use camino::Utf8PathBuf;
use std::fmt;
use std::fs::File;
use std::io::{self, BufRead, BufReader, Read};
#[derive(Default, Clone, Debug)]
pub enum FileOrStdin {
File(Utf8PathBuf),
#[default]
Stdin,
}
impl fmt::Display for FileOrStdin {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FileOrStdin::File(path) => write!(f, "{path}"),
FileOrStdin::Stdin => write!(f, "<stdin>"),
}
}
}
impl FileOrStdin {
#[must_use]
pub fn from_path(path: Utf8PathBuf) -> Self {
if path.as_str() == "-" {
FileOrStdin::Stdin
} else {
FileOrStdin::File(path)
}
}
#[must_use]
pub fn display(&self) -> String {
match self {
FileOrStdin::File(path) => path.to_string(),
FileOrStdin::Stdin => "<stdin>".to_string(),
}
}
pub fn reader(&self) -> Result<InputReader> {
match self {
FileOrStdin::File(path) => {
let file =
File::open(path).with_context(|| format!("failed to open file: {path}"))?;
Ok(InputReader::File(BufReader::new(file)))
}
FileOrStdin::Stdin => Ok(InputReader::Stdin(BufReader::new(io::stdin()))),
}
}
}
pub enum InputReader {
File(BufReader<File>),
Stdin(BufReader<io::Stdin>),
}
impl BufRead for InputReader {
fn fill_buf(&mut self) -> io::Result<&[u8]> {
match self {
InputReader::File(ref mut rdr) => rdr.fill_buf(),
InputReader::Stdin(ref mut rdr) => rdr.fill_buf(),
}
}
fn consume(&mut self, amt: usize) {
match self {
InputReader::File(ref mut rdr) => rdr.consume(amt),
InputReader::Stdin(ref mut rdr) => rdr.consume(amt),
}
}
}
impl Read for InputReader {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match self {
InputReader::File(ref mut rdr) => rdr.read(buf),
InputReader::Stdin(ref mut rdr) => rdr.read(buf),
}
}
}
impl InputReader {
pub fn read_to_string(&mut self) -> Result<String> {
let mut buf = String::new();
Read::read_to_string(self, &mut buf).context("failed to read input")?;
Ok(buf)
}
}