catfx 0.1.0

Apply effects to a text stream
Documentation
//! Utilities for the input type.
use std::ffi::OsStr;
use std::fs;
use std::io::{self, BufReader, Read};
use std::path::Path;
/// Helper for a file that is either STDIN or a filepath.
#[derive(Debug)]
pub enum File<'a> {
    /// The file is STDIN.
    Stdin(io::Stdin),
    /// The file is a path.
    Path(&'a Path),
}

impl<'a> File<'a> {
    /// Creates a `Stdin` variant.
    pub fn stdin() -> Self {
        Self::Stdin(io::stdin())
    }

    /// Creates a `Path` variant.
    pub fn path(path: &'a Path) -> Self {
        Self::Path(path)
    }

    /// Checks if the file is STDIN.
    pub const fn is_stdin(&self) -> bool {
        matches!(self, Self::Stdin(_))
    }

    /// Tries to get the path if it exists.
    pub const fn to_path(&self) -> Option<&'a Path> {
        match self {
            Self::Stdin(_) => None,
            Self::Path(p) => Some(p),
        }
    }

    /// Converts to a [`Reader`].
    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,
{
    /// Converts from an [`OsStr`] reference to a [`File`]. `-` will be treated as
    /// STDIN, and all other values will be treated as a path.
    fn from(value: &'a S) -> Self {
        if value.as_ref() == "-" {
            Self::Stdin(io::stdin())
        } else {
            Self::Path(Path::new(value))
        }
    }
}

/// A type that implements [`Read`].
pub enum Reader {
    /// Read from STDIN.
    Stdin(io::Stdin),
    /// Read from a file.
    File(fs::File),
}

impl Reader {
    /// Converts to a [`BufReader`].
    #[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)));
    }
}