pub struct Captures<'a> {
pub begin: &'a [u8],
pub data: &'a [u8],
pub end: &'a [u8],
}
pub fn parse_captures<'a>(input: &'a [u8]) -> Option<Captures<'a>> {
parser_inner(input).map(|(_, cap)| cap)
}
pub fn parse_captures_iter<'a>(input: &'a [u8]) -> CaptureMatches<'a> {
CaptureMatches { input }
}
pub struct CaptureMatches<'a> {
input: &'a [u8],
}
impl<'a> Iterator for CaptureMatches<'a> {
type Item = Captures<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.input.is_empty() {
return None;
}
match parser_inner(self.input) {
Some((remaining, captures)) => {
self.input = remaining;
Some(captures)
}
None => {
self.input = &[];
None
}
}
}
}
fn parse_begin<'a>(input: &'a [u8]) -> Option<(&'a [u8], &'a [u8])> {
let (input, _) = read_until(input, b"-----BEGIN ")?;
let (input, begin) = read_until(input, b"-----")?;
let input = skip_whitespace(input);
Some((input, begin))
}
fn parse_payload<'a>(input: &'a [u8]) -> Option<(&'a [u8], &'a [u8])> {
read_until(input, b"-----END ")
}
fn extract_headers_and_data<'a>(input: &'a [u8]) -> (&'a [u8], &'a [u8]) {
if let Some((rest, headers)) = read_until(input, b"\n\n") {
(headers, rest)
} else if let Some((rest, headers)) = read_until(input, b"\r\n\r\n") {
(headers, rest)
} else {
(&[], input)
}
}
fn parse_end<'a>(input: &'a [u8]) -> Option<(&'a [u8], &'a [u8])> {
let (remaining, end) = read_until(input, b"-----")?;
let remaining = skip_whitespace(remaining);
Some((remaining, end))
}
fn parser_inner<'a>(input: &'a [u8]) -> Option<(&'a [u8], Captures<'a>)> {
let (input, begin) = parse_begin(input)?;
let (input, payload) = parse_payload(input)?;
let (_headers, data) = extract_headers_and_data(payload);
let (remaining, end) = parse_end(input)?;
let captures = Captures { begin, data, end };
Some((remaining, captures))
}
fn skip_whitespace(mut input: &[u8]) -> &[u8] {
while let Some(b) = input.first() {
match b {
b' ' | b'\t' | b'\n' | b'\r' => {
input = &input[1..];
}
_ => break,
}
}
input
}
fn read_until<'a, 'b>(input: &'a [u8], marker: &'b [u8]) -> Option<(&'a [u8], &'a [u8])> {
if marker.is_empty() {
return Some((&[], input));
}
let mut index = 0;
let mut found = 0;
while input.len() - index >= marker.len() - found {
if input[index] == marker[found] {
found += 1;
} else {
found = 0;
}
index += 1;
if found == marker.len() {
let remaining = &input[index..];
let matched = &input[..index - found];
return Some((remaining, matched));
}
}
None
}