hg 0.3.0

Parser for the Mercury metalanguage
Documentation
use std::str::Bytes;

pub struct NewlineTerminatedBytes<'a> {
    bytes: Bytes<'a>,
    prev: Option<(usize, u8)>,
    offset: usize,
}

impl<'a> NewlineTerminatedBytes<'a> {
    #[inline(always)]
    pub fn new(bytes: Bytes<'a>) -> Self {
        Self {
            bytes, prev: None, offset: 0,
        }
    }
}

impl Iterator for NewlineTerminatedBytes<'_> {
    type Item = (usize, u8);

    #[inline(always)]
    fn next(&mut self) -> Option<Self::Item> {
        let next = self.bytes.next();
        match next {
            None => {
                match self.prev {
                    None => {
                        self.prev = Some((self.offset, b'\n'));
                    }
                    Some((_, b'\n')) => {
                        self.prev = None
                    }
                    Some((offset, _)) => {
                        self.prev = Some((offset + 1, b'\n'));
                    }
                }
            }
            Some(grapheme) => {
                self.prev = Some((self.offset, grapheme));
                self.offset += 1;
            }
        }
        self.prev
    }
}

#[cfg(test)]
mod tests {
    use crate::newline_terminated_bytes::NewlineTerminatedBytes;

    #[test]
    fn empty() {
        let str = "";
        let mut nt = NewlineTerminatedBytes::new(str.bytes());
        assert_eq!(Some((0, b'\n')), nt.next());
        assert_eq!(None, nt.next());
    }

    #[test]
    fn ending_with_newline() {
        let str = "hit\n";
        let mut nt = NewlineTerminatedBytes::new(str.bytes());
        assert_eq!(Some((0,  b'h')), nt.next());
        assert_eq!(Some((1,  b'i')), nt.next());
        assert_eq!(Some((2,  b't')), nt.next());
        assert_eq!(Some((3,  b'\n')), nt.next());
        assert_eq!(None, nt.next());
    }

    #[test]
    fn ending_with_extraneous_newline() {
        let str = "hit\n\n";
        let mut nt = NewlineTerminatedBytes::new(str.bytes());
        assert_eq!(Some((0,  b'h')), nt.next());
        assert_eq!(Some((1,  b'i')), nt.next());
        assert_eq!(Some((2,  b't')), nt.next());
        assert_eq!(Some((3,  b'\n')), nt.next());
        assert_eq!(Some((4,  b'\n')), nt.next());
        assert_eq!(None, nt.next());
    }

    #[test]
    fn ending_without_newline() {
        let str = "hit";
        let mut nt = NewlineTerminatedBytes::new(str.bytes());
        assert_eq!(Some((0,  b'h')), nt.next());
        assert_eq!(Some((1,  b'i')), nt.next());
        assert_eq!(Some((2,  b't')), nt.next());
        assert_eq!(Some((3,  b'\n')), nt.next());
        assert_eq!(None, nt.next());
    }
}