1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
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>)> {
    // Should be equivalent to the regex
    // "(?s)-----BEGIN (?P<begin>.*?)-----[ \t\n\r]*(?P<data>.*?)-----END (?P<end>.*?)-----[ \t\n\r]*"

    // (?s)                                      # Enable dotall (. matches all characters incl \n)
    // -----BEGIN (?P<begin>.*?)-----[ \t\n\r]*  # Parse begin
    // (?P<data>.*?)                             # Parse data
    // -----END (?P<end>.*?)-----[ \t\n\r]*      # Parse end

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

// Equivalent to the regex [ \t\n\r]*
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
}
// Equivalent to (.*?) followed by a string
// Returns the remaining input (after the secondary matched string) and the matched data
fn read_until<'a, 'b>(input: &'a [u8], marker: &'b [u8]) -> Option<(&'a [u8], &'a [u8])> {
    // If there is no end condition, short circuit
    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
}