#![deny(missing_docs)]
use {linereader::LineReader, std::io::Read};
mod bound;
pub trait ReadExt {
type Read: std::io::Read;
fn lines_rc_with_capacity(self, buffer_capacity: usize) -> bound::RcLineIterator<Self::Read>;
fn lines_rc(self) -> bound::RcLineIterator<Self::Read>;
}
impl<T: Read> ReadExt for T {
type Read = T;
fn lines_rc(self) -> bound::RcLineIterator<T> {
self.lines_rc_with_capacity(64 * 1024)
}
fn lines_rc_with_capacity(self, buffer_capacity: usize) -> bound::RcLineIterator<Self::Read> {
bound::RcLineIterator::new(
LineReader::with_capacity(buffer_capacity, self),
buffer_capacity,
)
}
}
#[derive(thiserror::Error, Debug)]
pub enum Error<T: std::fmt::Debug> {
#[error("io")]
Io(#[from] std::io::Error),
#[error("encoding")]
Encoding(#[from] std::str::Utf8Error),
#[error("Incomplete line")]
Incomplete(T),
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::{BufRead, BufReader, Cursor};
#[test]
fn return_whitespace_line() {
assert_behave_same(&" \r\n");
assert_behave_same(&" \n");
}
#[test]
fn return_whitespace_on_last_line() {
assert_behave_same(&"\r\n ");
assert_behave_same(&"\n ");
}
#[test]
fn return_trailing_double_linebkreak() {
assert_behave_same(&"\r\n\r\n");
assert_behave_same(&"\n\n");
}
#[test]
fn return_whitespace_line_and_terminating_a() {
assert_behave_same(&"\r\na")
}
#[test]
fn assert_non_ascii_returns_error() {
let buf = ['a' as u8, 'b' as u8, 254];
assert_behave_same(&buf);
}
fn assert_behave_same<T: AsRef<[u8]>>(input: &T) {
let mut own_iter = BufReader::new(Cursor::new(input)).lines();
let mut rc_iter = std::io::Cursor::new(input).lines_rc();
for (own_line, rc_line) in own_iter.by_ref().zip(rc_iter.by_ref()) {
match own_line {
Ok(o) => assert_eq!(o, *rc_line.unwrap()),
Err(_) => {
rc_line.unwrap_err();
}
}
}
assert_eq!(own_iter.count(), 0);
assert_eq!(rc_iter.count(), 0);
}
}