1pub struct Captures<'a> {
2 pub begin: &'a [u8],
3 pub headers: &'a [u8],
4 pub data: &'a [u8],
5 pub end: &'a [u8],
6}
7
8pub fn parse_captures(input: &[u8]) -> Option<Captures<'_>> {
9 parser_inner(input).map(|(_, cap)| cap)
10}
11pub fn parse_captures_iter(input: &[u8]) -> CaptureMatches<'_> {
12 CaptureMatches { input }
13}
14
15pub struct CaptureMatches<'a> {
16 input: &'a [u8],
17}
18impl<'a> Iterator for CaptureMatches<'a> {
19 type Item = Captures<'a>;
20 fn next(&mut self) -> Option<Self::Item> {
21 if self.input.is_empty() {
22 return None;
23 }
24 match parser_inner(self.input) {
25 Some((remaining, captures)) => {
26 self.input = remaining;
27 Some(captures)
28 }
29 None => {
30 self.input = &[];
31 None
32 }
33 }
34 }
35}
36
37fn parse_begin(input: &[u8]) -> Option<(&[u8], &[u8])> {
38 let (input, _) = read_until(input, b"-----BEGIN ")?;
39 let (input, begin) = read_until(input, b"-----")?;
40 let input = skip_whitespace(input);
41 Some((input, begin))
42}
43
44fn parse_payload(input: &[u8]) -> Option<(&[u8], &[u8])> {
45 read_until(input, b"-----END ")
46}
47
48fn extract_headers_and_data(input: &[u8]) -> (&[u8], &[u8]) {
49 if let Some((rest, headers)) = read_until(input, b"\n\n") {
50 (headers, rest)
51 } else if let Some((rest, headers)) = read_until(input, b"\r\n\r\n") {
52 (headers, rest)
53 } else {
54 (&[], input)
55 }
56}
57
58fn parse_end(input: &[u8]) -> Option<(&[u8], &[u8])> {
59 let (remaining, end) = read_until(input, b"-----")?;
60 let remaining = skip_whitespace(remaining);
61 Some((remaining, end))
62}
63
64fn parser_inner(input: &[u8]) -> Option<(&[u8], Captures<'_>)> {
65 let (input, begin) = parse_begin(input)?;
74 let (input, payload) = parse_payload(input)?;
75 let (headers, data) = extract_headers_and_data(payload);
76 let (remaining, end) = parse_end(input)?;
77
78 let captures = Captures {
79 begin,
80 headers,
81 data,
82 end,
83 };
84 Some((remaining, captures))
85}
86
87fn skip_whitespace(mut input: &[u8]) -> &[u8] {
89 while let Some(b) = input.first() {
90 match b {
91 b' ' | b'\t' | b'\n' | b'\r' => {
92 input = &input[1..];
93 }
94 _ => break,
95 }
96 }
97 input
98}
99fn read_until<'a>(input: &'a [u8], marker: &[u8]) -> Option<(&'a [u8], &'a [u8])> {
102 if marker.is_empty() {
104 return Some((&[], input));
105 }
106 let mut index = 0;
107 let mut found = 0;
108 while input.len() - index >= marker.len() - found {
109 if input[index] == marker[found] {
110 found += 1;
111 } else {
112 found = 0;
113 }
114 index += 1;
115 if found == marker.len() {
116 let remaining = &input[index..];
117 let matched = &input[..index - found];
118 return Some((remaining, matched));
119 }
120 }
121 None
122}