Coverage Report

Created: 2025-02-25 06:39

/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
}