#[derive(Debug)]
pub enum RawEventLine<'a> {
Comment,
Field(&'a str, Option<&'a str>),
Empty,
}
#[inline]
pub fn is_lf(c: char) -> bool {
c == '\u{000A}'
}
#[inline]
pub fn is_bom(c: char) -> bool {
c == '\u{feff}'
}
fn find_eol(bytes: &[u8]) -> Option<(usize, usize)> {
const CR: u8 = b'\r';
const LF: u8 = b'\n';
let first_match = memchr::memchr2(CR, LF, bytes)?;
match bytes[first_match] {
LF => Some((first_match, first_match + 1)),
CR => {
if first_match + 1 >= bytes.len() {
return None; }
if bytes[first_match + 1] == LF {
Some((first_match, first_match + 2))
} else {
Some((first_match, first_match + 1))
}
}
_ => unreachable!(),
}
}
pub fn line(input: &str) -> Option<(&str, RawEventLine<'_>)> {
let (line_end, rem_start) = find_eol(input.as_bytes())?;
let line = &input[..line_end];
let rem = &input[rem_start..];
if line.is_empty() {
return Some((rem, RawEventLine::Empty));
}
match memchr::memchr(b':', line.as_bytes()) {
Some(0) => Some((rem, RawEventLine::Comment)),
Some(colon_pos) => {
let value_start = if line.as_bytes().get(colon_pos + 1) == Some(&b' ') {
colon_pos + 2
} else {
colon_pos + 1
};
Some((
rem,
RawEventLine::Field(&line[..colon_pos], Some(&line[value_start..])),
))
}
None => Some((rem, RawEventLine::Field(line, None))),
}
}