eolify 0.4.0

High-performance line ending normalization for Rust.
Documentation
use std::io::Read;

use eolify::{IoExt, ReadExt, LF};

#[test]
fn crlf_split_across_readers() {
    let readers = vec![b"foo\r".as_ref(), b"\nbar".as_ref()].into_iter();
    let test_reader = TestReader::new(readers);
    let nr = LF::wrap_reader_with_buffer_size(test_reader, 3);
    let out = read_all(nr);
    assert_eq!(out, b"foo\nbar".to_vec());
}

#[test]
fn crlf_split_across_three_readers() {
    let readers = vec![b"\r".as_ref(), b"".as_ref(), b"\n".as_ref()].into_iter();
    let test_reader = TestReader::new(readers);
    let nr = LF::wrap_reader_with_buffer_size(test_reader, 3);
    let out = read_all(nr);
    assert_eq!(out, b"\n".to_vec());
}

#[test]
fn lone_lf_in_first_reader_kept_as_lf() {
    let readers = vec![b"line1\n".as_ref(), b"line2".as_ref()].into_iter();
    let test_reader = TestReader::new(readers);
    let nr = LF::wrap_reader_with_buffer_size(test_reader, 4);
    let out = read_all(nr);
    assert_eq!(out, b"line1\nline2".to_vec());
}

#[test]
fn multiple_crs_and_crlf_mixed_across_boundaries() {
    let readers = vec![b"\r".as_ref(), b"\r\n".as_ref()].into_iter();
    let test_reader = TestReader::new(readers);
    let nr = LF::wrap_reader_with_buffer_size(test_reader, 2);
    let out = read_all(nr);
    assert_eq!(out, b"\n\n".to_vec());
}

#[test]
fn trailing_cr_at_eof_emits_lf() {
    let readers = vec![b"foo\r".as_ref()].into_iter();
    let test_reader = TestReader::new(readers);
    let nr = LF::wrap_reader_with_buffer_size(test_reader, 4);
    let out = read_all(nr);
    assert_eq!(out, b"foo\n".to_vec());
}

#[test]
fn extension_trait() {
    let readers = vec![b"\r".as_ref(), b"\r\n".as_ref()].into_iter();
    let test_reader = TestReader::new(readers);
    let nr = test_reader.normalize_newlines(LF);
    let out = read_all(nr);
    assert_eq!(out, b"\n\n".to_vec());
}

fn read_all<R: Read>(mut r: R) -> Vec<u8> {
    let mut out = Vec::new();
    r.read_to_end(&mut out).unwrap();
    out
}

pub struct TestReader<R, I> {
    readers: I,
    current: Option<R>,
}

impl<R: Read, I: Iterator<Item = R>> TestReader<R, I> {
    pub fn new(mut readers: I) -> TestReader<R, I> {
        let current = readers.next();
        TestReader {
            readers: readers,
            current: current,
        }
    }
}

impl<R: Read, I: Iterator<Item = R>> Read for TestReader<R, I> {
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        loop {
            match self.current {
                Some(ref mut r) => {
                    let n = r.read(buf)?;
                    if n > 0 {
                        return Ok(n);
                    }
                }
                None => return Ok(0),
            }
            self.current = self.readers.next();
        }
    }
}