use std::ffi::OsStr;
use std::fs;
use std::io::{self, BufReader, Read};
use std::path::Path;
#[derive(Debug)]
pub enum File<'a> {
Stdin(io::Stdin),
Path(&'a Path),
}
impl<'a> File<'a> {
pub fn stdin() -> Self {
Self::Stdin(io::stdin())
}
pub fn path(path: &'a Path) -> Self {
Self::Path(path)
}
pub const fn is_stdin(&self) -> bool {
matches!(self, Self::Stdin(_))
}
pub const fn to_path(&self) -> Option<&'a Path> {
match self {
Self::Stdin(_) => None,
Self::Path(p) => Some(p),
}
}
pub fn into_reader(self) -> io::Result<Reader> {
let reader = match self {
File::Stdin(stdin) => Reader::Stdin(stdin),
File::Path(path) => {
let file = fs::File::open(path)?;
Reader::File(file)
}
};
Ok(reader)
}
}
impl<'a, S> From<&'a S> for File<'a>
where
S: AsRef<OsStr> + ?Sized,
{
fn from(value: &'a S) -> Self {
if value.as_ref() == "-" {
Self::Stdin(io::stdin())
} else {
Self::Path(Path::new(value))
}
}
}
pub enum Reader {
Stdin(io::Stdin),
File(fs::File),
}
impl Reader {
#[inline]
pub fn into_buf_reader(self) -> BufReader<Self> {
BufReader::new(self)
}
}
impl Read for Reader {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match self {
Self::Stdin(stdin) => stdin.read(buf),
Self::File(file) => file.read(buf),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_from_hyphen_string() {
let file = File::from("-");
assert!(file.is_stdin());
assert!(file.to_path().is_none());
}
#[test]
fn test_from_string() {
const PATH: &str = "path/to/file.ext";
let file = File::from(PATH);
assert!(!file.is_stdin());
assert_eq!(file.to_path(), Some(Path::new(PATH)));
}
}