specker 0.3.5

Testing utility that simplifies file matching against bunch of templates.
Documentation
// Copyright 2017 Nerijus Arlauskas
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use error::{FilePosition, LexResult};

pub struct Contents<'a> {
    pub slice: &'a [u8],
    pub lo: FilePosition,
    pub hi: FilePosition,
}

impl<'a> Contents<'a> {
    pub fn new<'r>(input: &'r [u8], lo: FilePosition, hi: FilePosition) -> Contents<'r> {
        Contents {
            slice: &input[lo.byte..hi.byte],
            lo: lo,
            hi: hi,
        }
    }

    pub fn trimmed(self) -> Contents<'a> {
        let mut start = 0;
        let mut end = self.slice.len();
        while start < end {
            if b" \t".contains(&self.slice[start]) {
                start += 1;
            } else {
                break;
            }
        }
        while end > start {
            if b" \t".contains(&self.slice[end - 1]) {
                end -= 1;
            } else {
                break;
            }
        }

        Contents {
            slice: &self.slice[start..end],
            lo: self.lo.advanced(start),
            hi: self.lo.advanced(end),
        }
    }
}

pub fn check_new_line(cursor: &mut FilePosition, input: &[u8]) -> bool {
    if input[cursor.byte..].starts_with(b"\r\n") {
        cursor.next_line(2);
        return true;
    }
    if input[cursor.byte..].starts_with(b"\n") {
        cursor.next_line(1);
        return true;
    }
    return false;
}

pub fn check_exact_bytes<'e>(cursor: &mut FilePosition, input: &[u8], other: &'e [u8]) -> bool {
    if input[cursor.byte..].starts_with(other) {
        cursor.advance(other.len());
        return true;
    }
    false
}

pub fn check_eof(cursor: &mut FilePosition, input: &[u8]) -> bool {
    cursor.byte >= input.len()
}

pub enum TermType {
    Sequence,
    EolOrEof,
}

pub fn expect_text<'a>(cursor: &mut FilePosition, input: &'a [u8]) -> LexResult<Contents<'a>> {
    let start_cursor = cursor.clone();
    let mut end = start_cursor.byte;
    loop {
        if end >= input.len() || input[end..].starts_with(b"\n")
            || input[end..].starts_with(b"\r\n")
        {
            break;
        }

        end += 1;
    }

    cursor.advance(end - start_cursor.byte);
    return Ok(Contents::new(input, start_cursor, *cursor));
}

pub fn expect_terminated_text<'a, 'e>(
    cursor: &mut FilePosition,
    input: &'a [u8],
    term_sequence: &'e [u8],
) -> LexResult<(Contents<'a>, TermType)> {
    let start_cursor = cursor.clone();
    let mut end = start_cursor.byte;
    loop {
        if end >= input.len() || input[end..].starts_with(b"\n")
            || input[end..].starts_with(b"\r\n")
        {
            break;
        }
        if input[end..].starts_with(term_sequence) {
            let end_cursor = cursor.advanced(end - start_cursor.byte);
            cursor.advance(end - start_cursor.byte + term_sequence.len());
            return Ok((
                Contents::new(input, start_cursor, end_cursor),
                TermType::Sequence,
            ));
        }

        end += 1;
    }

    cursor.advance(end - start_cursor.byte);
    return Ok((
        Contents::new(input, start_cursor, *cursor),
        TermType::EolOrEof,
    ));
}

#[cfg(test)]
mod tests {
    use super::*;
    use error::FilePosition;

    fn trim(text: &[u8]) -> &[u8] {
        let c = Contents::new(
            text,
            FilePosition::new(),
            FilePosition::new().advanced(text.len()),
        );
        c.trimmed().slice
    }

    fn trim_pos<'a>(text: &'a [u8]) -> Contents<'a> {
        let c = Contents::new(
            text,
            FilePosition::new(),
            FilePosition::new().advanced(text.len()),
        );
        c.trimmed()
    }

    #[test]
    fn test_trim() {
        assert_eq!(trim(b""), b"");
        assert_eq!(trim(b" "), b"");
        assert_eq!(trim(b"\t"), b"");
        assert_eq!(trim(b"a"), b"a");
        assert_eq!(trim(b" a"), b"a");
        assert_eq!(trim(b"a "), b"a");
        assert_eq!(trim(b" a "), b"a");
    }

    #[test]
    fn test_trim_position() {
        let trimmed = trim_pos(b" d ");
        assert_eq!(trimmed.slice, b"d");
        assert_eq!(trimmed.lo.byte, 1);
        assert_eq!(trimmed.hi.byte, 2);
    }
}