qread 0.1.0

A convenience library for common read operations
Documentation
//! `qread` adds shortcut functionality to anything that implements the [`std::io::Read`]
//! or [`std::io::BufRead`] traits to make certain read operations more ergonomic by handling
//! allocation of a buffer for you.  Specifically, `Read` implementors get two new methods:
//! 
//! * [`read_all_to_bytes`](QRead::read_all_to_bytes), which calls `.read_to_end()` on the object and returns the vector of
//!   bytes gotten from reading the object.
//! * [`read_all_to_string`](QRead::read_all_to_string), which calls `.read_to_string()` on the object and returns the string
//!   created.
//! 
//! Likewise, `ReadBuf` implementors also get two new methods:
//! 
//! * [`read_until_to_bytes`](QBufRead::read_until_to_bytes`), which calls `.read_until()` on the object and returns the vector of
//!   bytes gotten from the object.
//! * [`read_line_to_string`](QBufRead::read_line_to_string) which calls `.read_line()` on the object and returns the line as a
//!   string (note that it will include the newline).
//! 
//! 
//! # Examples
//! 
//! Using [`read_all_to_bytes`](QRead::read_all_to_bytes) and [`read_all_to_string`](QRead::read_all_to_string) to read the full
//! contents of a file as a vector of bytes or a string, respectively, is very straightforward. Note that these are both members
//! of the [`QRead`] trait, so that trait must be brought into scope to use these methods.
//! 
//! ```no_run
//! use std::fs::File;
//! use qread::QRead;
//! 
//! // This assumes that we have a file `demo.bin` in the current directory
//! let mut f1 = File::open("demo.bin").unwrap();
//! let data = f1.read_all_to_bytes().unwrap();
//! 
//! // This assumes that we have a file `demo.txt` in the current directory
//! let mut f2 = File::open("demo.txt").unwrap();
//! let text = f2.read_all_to_string().unwrap();
//! ```
//! 
//! 
//! Likewise, the [`read_until_to_bytes`](QBufRead::read_until_to_bytes`) and [`read_line_to_string`](QBufRead::read_line_to_string)
//! methods are also straightforward, provided that the [`QBufRead`] trait is in scope.
//! 
//! ```no_run
//! use std::fs::File;
//! use std::io::BufReader;
//! use qread::QBufRead;
//! 
//! // This assumes that we have a file `demo.bin` in the current directory
//! let mut f1 = BufReader::new(File::open("demo.bin").unwrap());
//! // Read up to the first instance of byte = 97 (a lowercase ASCII "a")
//! let data = f1.read_until_to_bytes(97).unwrap();
//! 
//! // This assumes that we have a file `demo.txt` in the current directory
//! let mut f2 = BufReader::new(File::open("demo.txt").unwrap());
//! let text = f2.read_line_to_string().unwrap();
//! ```
//! 
//! # Implementing `QRead` and `QBufRead`
//! 
//! You should not need to implement these traits on your own custom objects. Instead, implement [`Read`](std::io::Read)
//! and [`BufRead`](std::io::BufRead), respectively. Because [`QRead`] and [`QBufRead`] have blanket implementations for
//! any types implementing `Read` and `BufRead`, respectively, implementing the latter traits automatically grants the former.

use std::io::{Result,Read,BufRead};

/// Trait for implementing ergonomic reads of full file content
/// 
/// This trait implements wrappers around two of the methods from [`std::io::Read`]
/// to handle allocating the necessary buffer for you.
pub trait QRead {
    /// Reads all the contents of the file or other readable object as bytes.
    /// 
    /// This will return the full remaining content of the data in a new vector
    /// of bytes, handling the creation of the vector for you. It only returns an
    /// `Err` if the underlying call to [`std::io::Read::read_to_end`] does.
    fn read_all_to_bytes(&mut self) -> Result<Vec<u8>>;

    /// Reads all the contents of the file or other readable object as a string.
    /// 
    /// This will return the full remaining content of the data in a new string,
    /// handling the creation of the string for you. It only returns an `Err` if
    /// the underlying call to [`std::io::Read::read_to_string`] does.
    fn read_all_to_string(&mut self) -> Result<String>;
}

impl<T: Read> QRead for T {
    fn read_all_to_bytes(&mut self) -> Result<Vec<u8>> {
        let mut buf = Vec::new();
        self.read_to_end(&mut buf)?;
        Ok(buf)
    }

    fn read_all_to_string(&mut self) -> Result<String> {
        let mut buf = String::new();
        self.read_to_string(&mut buf)?;
        Ok(buf)
    }
}

/// Trait for implementing ergonomic reads of buffered file content
/// 
/// This trait implements wrappers around two of the methods from [`std::io::BufRead`]
/// to handle allocating the necessary buffer for you.
pub trait QBufRead {
    /// Read the content until `byte` or the content end is reached and returns the bytes read.
    /// 
    /// This will return the bytes from the current position in the data up to and including the
    /// next instance of `byte` as a new vector of bytes. It only returns an `Err` if the underlying
    /// call to [`std::io::BufRead::read_until`] does.
    fn read_until_to_bytes(&mut self, byte: u8) -> Result<Vec<u8>>;

    /// Read the content of the next line of the data into a new string.
    /// 
    /// This will return the next line of the file (or other readable object) as a new string, which
    /// it allocates for you. It only returns an `Err` if the underlying call to [`std::io::BufRead::read_line`]
    /// does.
    fn read_line_to_string(&mut self) -> Result<String>;
}

impl <T: BufRead> QBufRead for T {
    fn read_until_to_bytes(&mut self, byte: u8) -> Result<Vec<u8>> {
        let mut buf = Vec::new();
        self.read_until(byte, &mut buf)?;
        Ok(buf)
    }

    fn read_line_to_string(&mut self) -> Result<String> {
        let mut buf = String::new();
        self.read_line(&mut buf)?;
        Ok(buf)
    }
}


#[cfg(test)]
mod tests {
    use super::*;
    use std::io::Cursor;

    #[test]
    fn qread_bytes() {
        let v = vec![100,102,104,106,108];
        let mut h = Cursor::new(v.clone());

        let v2 = h.read_all_to_bytes().unwrap();
        assert_eq!(v, v2)
    }

    #[test]
    fn qread_string() {
        let s = "Hello Ferris!".to_owned();
        let mut h = Cursor::new(s.clone());
        let s2 = h.read_all_to_string().unwrap();
        assert_eq!(s, s2)
    }

    #[test]
    fn qbufread_bytes() {
        let v = vec![100,102,104,106,108];
        let mut h = Cursor::new(v.clone());
        let v2 = h.read_until_to_bytes(104).unwrap();
        assert_eq!(v[..3], v2)
    }

    #[test]
    fn qbufread_string() {
        let s = "Hello Ferris!\nGoodbye Ferris!";
        let mut h = Cursor::new(s.to_owned());
        let s2 = h.read_line_to_string().unwrap();
        assert_eq!(s2, "Hello Ferris!\n")
    }
}