/rust/registry/src/index.crates.io-6f17d22bba15001f/pem-3.0.4/src/parser.rs
Line | Count | Source (jump to first uncovered line) |
1 | | pub 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 | | |
8 | 0 | pub fn parse_captures(input: &[u8]) -> Option<Captures<'_>> { |
9 | 0 | parser_inner(input).map(|(_, cap)| cap) |
10 | 0 | } |
11 | 0 | pub fn parse_captures_iter(input: &[u8]) -> CaptureMatches<'_> { |
12 | 0 | CaptureMatches { input } |
13 | 0 | } |
14 | | |
15 | | pub struct CaptureMatches<'a> { |
16 | | input: &'a [u8], |
17 | | } |
18 | | impl<'a> Iterator for CaptureMatches<'a> { |
19 | | type Item = Captures<'a>; |
20 | 0 | fn next(&mut self) -> Option<Self::Item> { |
21 | 0 | if self.input.is_empty() { |
22 | 0 | return None; |
23 | 0 | } |
24 | 0 | match parser_inner(self.input) { |
25 | 0 | Some((remaining, captures)) => { |
26 | 0 | self.input = remaining; |
27 | 0 | Some(captures) |
28 | | } |
29 | | None => { |
30 | 0 | self.input = &[]; |
31 | 0 | None |
32 | | } |
33 | | } |
34 | 0 | } |
35 | | } |
36 | | |
37 | 0 | fn parse_begin(input: &[u8]) -> Option<(&[u8], &[u8])> { |
38 | 0 | let (input, _) = read_until(input, b"-----BEGIN ")?; |
39 | 0 | let (input, begin) = read_until(input, b"-----")?; |
40 | 0 | let input = skip_whitespace(input); |
41 | 0 | Some((input, begin)) |
42 | 0 | } |
43 | | |
44 | 0 | fn parse_payload(input: &[u8]) -> Option<(&[u8], &[u8])> { |
45 | 0 | read_until(input, b"-----END ") |
46 | 0 | } |
47 | | |
48 | 0 | fn extract_headers_and_data(input: &[u8]) -> (&[u8], &[u8]) { |
49 | 0 | if let Some((rest, headers)) = read_until(input, b"\n\n") { |
50 | 0 | (headers, rest) |
51 | 0 | } else if let Some((rest, headers)) = read_until(input, b"\r\n\r\n") { |
52 | 0 | (headers, rest) |
53 | | } else { |
54 | 0 | (&[], input) |
55 | | } |
56 | 0 | } |
57 | | |
58 | 0 | fn parse_end(input: &[u8]) -> Option<(&[u8], &[u8])> { |
59 | 0 | let (remaining, end) = read_until(input, b"-----")?; |
60 | 0 | let remaining = skip_whitespace(remaining); |
61 | 0 | Some((remaining, end)) |
62 | 0 | } |
63 | | |
64 | 0 | fn parser_inner(input: &[u8]) -> Option<(&[u8], Captures<'_>)> { |
65 | | // Should be equivalent to the regex |
66 | | // "(?s)-----BEGIN (?P<begin>.*?)-----[ \t\n\r]*(?P<data>.*?)-----END (?P<end>.*?)-----[ \t\n\r]*" |
67 | | |
68 | | // (?s) # Enable dotall (. matches all characters incl \n) |
69 | | // -----BEGIN (?P<begin>.*?)-----[ \t\n\r]* # Parse begin |
70 | | // (?P<data>.*?) # Parse data |
71 | | // -----END (?P<end>.*?)-----[ \t\n\r]* # Parse end |
72 | | |
73 | 0 | let (input, begin) = parse_begin(input)?; |
74 | 0 | let (input, payload) = parse_payload(input)?; |
75 | 0 | let (headers, data) = extract_headers_and_data(payload); |
76 | 0 | let (remaining, end) = parse_end(input)?; |
77 | | |
78 | 0 | let captures = Captures { |
79 | 0 | begin, |
80 | 0 | headers, |
81 | 0 | data, |
82 | 0 | end, |
83 | 0 | }; |
84 | 0 | Some((remaining, captures)) |
85 | 0 | } |
86 | | |
87 | | // Equivalent to the regex [ \t\n\r]* |
88 | 0 | fn skip_whitespace(mut input: &[u8]) -> &[u8] { |
89 | 0 | while let Some(b) = input.first() { |
90 | 0 | match b { |
91 | 0 | b' ' | b'\t' | b'\n' | b'\r' => { |
92 | 0 | input = &input[1..]; |
93 | 0 | } |
94 | 0 | _ => break, |
95 | | } |
96 | | } |
97 | 0 | input |
98 | 0 | } |
99 | | // Equivalent to (.*?) followed by a string |
100 | | // Returns the remaining input (after the secondary matched string) and the matched data |
101 | 0 | fn read_until<'a>(input: &'a [u8], marker: &[u8]) -> Option<(&'a [u8], &'a [u8])> { |
102 | 0 | // If there is no end condition, short circuit |
103 | 0 | if marker.is_empty() { |
104 | 0 | return Some((&[], input)); |
105 | 0 | } |
106 | 0 | let mut index = 0; |
107 | 0 | let mut found = 0; |
108 | 0 | while input.len() - index >= marker.len() - found { |
109 | 0 | if input[index] == marker[found] { |
110 | 0 | found += 1; |
111 | 0 | } else { |
112 | 0 | found = 0; |
113 | 0 | } |
114 | 0 | index += 1; |
115 | 0 | if found == marker.len() { |
116 | 0 | let remaining = &input[index..]; |
117 | 0 | let matched = &input[..index - found]; |
118 | 0 | return Some((remaining, matched)); |
119 | 0 | } |
120 | | } |
121 | 0 | None |
122 | 0 | } |