1use core::str::Chars;
2use core::iter::{Map, once};
3
4use {Void, map_chars, is_whitespace};
5
6#[derive(Debug, PartialEq)]
7pub enum EncapsulationBoundaryError<Location, LabelError> {
8 MissingExpected(char),
9 Mismatch {
10 location: Location,
11 expected: char,
12 found: char,
13 },
14 LabelError{
15 location: Location,
16 error: LabelError
17 },
18}
19
20pub enum BoundaryType {
22 Begin,
23 End,
24}
25pub trait Label {
27 type LabelError;
29
30 fn push(&mut self, found: char) -> Result<Option<char>, Self::LabelError>;
35
36 fn complete(&mut self) -> Result<Option<char>, Self::LabelError> {
42 Ok(None)
43 }
44}
45
46pub struct DiscardLabel;
48
49pub struct LabelFn<F>(pub F);
51
52pub struct LabelMatcher<I>(pub I);
54
55impl<'a, T: 'a+Extend<char>> Label for &'a mut T {
56 type LabelError = Void;
57 fn push(&mut self, found: char) -> Result<Option<char>, Self::LabelError> {
58 self.extend(once(found));
59 Ok(None)
60 }
61}
62
63impl Label for DiscardLabel {
64 type LabelError = Void;
65 fn push(&mut self, _: char) -> Result<Option<char>, Self::LabelError> {
66 Ok(None)
67 }
68}
69
70impl <T, F> Label for LabelFn<F>
71where F: FnMut(char) -> Result<Option<char>, T> {
72 type LabelError = T;
73 fn push(&mut self, found: char) -> Result<Option<char>, T> {
74 self.0(found)
75 }
76}
77
78impl<T: Iterator<Item=char>> Label for LabelMatcher<T> {
79 type LabelError = Void;
80 fn push(&mut self, found: char) -> Result<Option<char>, Self::LabelError> {
81 Ok(match self.0.next() {
82 Some(expected) => if expected == found {
83 None
84 } else {
85 Some(expected)
86 },
87 None => Some(found),
88 })
89 }
90
91 fn complete(&mut self) -> Result<Option<char>, Self::LabelError> {
92 Ok(self.0.next())
93 }
94}
95
96
97
98pub struct BoundaryParser<Loc, Lbl: Label, S> {
99 stream: S,
100 state: Option<BoundaryParserState<Loc, Lbl>>,
101 result: Result<(),EncapsulationBoundaryError<Loc, Lbl::LabelError>>,
102}
103
104enum BoundaryParserState<Loc, Lbl> {
105 EatFirst{
106 label: Lbl,
107 b: BoundaryType,
108 },
109 NotEatFirst(BoundaryParserState2<Loc, Lbl>),
110}
111
112enum BoundaryParserState2<Loc, Lbl> {
113 EatKey{
114 label: Lbl,
115 key: Chars<'static>,
116 expected: char,
117 },
118 NotEatKey(BoundaryParserState3<Loc, Lbl>)
119}
120
121enum BoundaryParserState3<Loc, Lbl> {
122 EatLabel{
123 label: Lbl,
124 prev_dash: Option<Loc>,
125 },
126 EatEnd{
127 end: Chars<'static>,
128 expected: char,
129 },
130}
131
132impl<Loc, Lbl, E, S> BoundaryParser<Loc, Lbl, S>
133where Lbl: Label,
134 S: Iterator<Item = Result<(Loc, char), E>>
135 {
136 pub fn new(b: BoundaryType, stream: S, label: Lbl) -> Self {
137 BoundaryParser{
138 stream, state: Some(BoundaryParserState::EatFirst{label, b}), result: Ok(()),
139 }
140 }
141
142 pub fn complete(self) -> Result<(), EncapsulationBoundaryError<Loc, Lbl::LabelError>> {
144 self.result
145 }
146}
147
148impl<Loc, Lbl, S> BoundaryParser<Loc, Lbl, Map<S, fn((Loc, char)) -> Result<(Loc, char), Void>>>
149where Lbl: Label,
150 S: Iterator<Item = (Loc, char)>
151 {
152 pub fn from_chars(b: BoundaryType, stream: S, label: Lbl) -> Self {
153 Self::new(b, stream.map(map_chars), label)
154 }
155}
156
157impl<Loc, Lbl, E, S> Iterator for BoundaryParser<Loc, Lbl, S>
158where Lbl: Label,
159 S: Iterator<Item = Result<(Loc, char), E>>
160{
161 type Item = E;
162 fn next(&mut self) -> Option<E> {
164 match self.state.take().unwrap().process(&mut self.stream) {
165 Err(e) => {
166 self.result = Err(e);
167 None
168 },
169 Ok(None) => None,
170 Ok(Some((s, e))) => {
171 self.state = Some(s);
172 Some(e)
173 },
174 }
175 }
176}
177
178impl<Loc, Lbl: Label> BoundaryParserState<Loc, Lbl> {
179 fn process<'a, E: 'a>(self, stream: &'a mut Iterator<Item = Result<(Loc, char), E>>) -> Result<Option<(Self, E)>, EncapsulationBoundaryError<Loc, Lbl::LabelError>> {
180 use self::EncapsulationBoundaryError::*;
181 use self::BoundaryParserState::*;
182 use self::BoundaryParserState2::*;
183
184
185 let v = match self {
186 EatFirst{label, b} => {
187 let key = match b {
190 BoundaryType::Begin => {
191 match stream.skip_while(|c| c.as_ref().ok().map_or(false, is_whitespace)).next() {
192 Some(Err(e)) => return Ok(Some((EatFirst{label, b}, e))),
193 None => return Err(MissingExpected('-')),
194 Some(Ok((location, found))) => if found != '-' {
195 return Err(Mismatch{found, location, expected: '-'})
196 },
197 }
198 "---BEGIN "
199 },
200 BoundaryType::End => "---END ",
201 }.chars();
202
203 EatKey{label, key, expected: '-'}
204 },
205 NotEatFirst(v) => v,
206 };
207
208 v.process(stream).map(|v| v.map(|(v, e)| (NotEatFirst(v), e)))
209 }
210}
211
212impl<Loc, Lbl: Label> BoundaryParserState2<Loc, Lbl> {
213 fn process<'a, E: 'a>(self, stream: &'a mut Iterator<Item = Result<(Loc, char), E>>) -> Result<Option<(Self, E)>, EncapsulationBoundaryError<Loc, Lbl::LabelError>> {
214 use self::EncapsulationBoundaryError::*;
215 use self::BoundaryParserState2::*;
216 use self::BoundaryParserState3::*;
217
218 let v = match self {
219 EatKey{label, mut key, mut expected} => loop {
220 match stream.next() {
221 Some(Err(e)) => return Ok(Some((EatKey{label, key, expected}, e))),
222 None => return Err(MissingExpected(expected)),
223 Some(Ok((location, found))) => if found != expected {
224 return Err(Mismatch{found, location, expected})
225 } else if let Some(e) = key.next() {
226 expected = e;
227 } else {
228 break EatLabel{label, prev_dash: None}
229 },
230 }
231 },
232 NotEatKey(v) => v,
233 };
234
235 v.process(stream).map(|v| v.map(|(v, e)| (NotEatKey(v), e)))
236 }
237}
238
239impl<Loc, Lbl: Label> BoundaryParserState3<Loc, Lbl> {
240 fn process<'a, E: 'a>(self, stream: &'a mut Iterator<Item = Result<(Loc, char), E>>) -> Result<Option<(Self, E)>, EncapsulationBoundaryError<Loc, Lbl::LabelError>> {
241 use self::EncapsulationBoundaryError::*;
242 use self::BoundaryParserState3::*;
243
244 let (mut end, mut expected) = match self {
245 EatLabel{mut label, mut prev_dash} => loop {
246 use self::EncapsulationBoundaryError::*;
247
248 let v = stream.next();
249 let (location, c) = match v {
250 Some(Err(e)) => return Ok(Some((EatLabel{label, prev_dash}, e))),
251 None => return Err(MissingExpected('-')),
252 Some(Ok(c)) => c,
253 };
254
255 if c == '-' {
257 if prev_dash.is_none() {
258 prev_dash = Some(location);
259 continue;
260 }
261
262 match label.complete() {
263 Ok(None) => {},
264 Err(error) => return Err(LabelError{error, location}),
265 Ok(Some(expected)) => return Err(Mismatch{location, expected, found: c}),
266 }
267
268 break ("--".chars(), '-');
270 }
271
272 if let Some(prev_location) = prev_dash.take() {
274 match label.push('-') {
275 Ok(None) => {},
276 Err(error) => return Err(LabelError{error, location: prev_location}),
277 Ok(Some(expected)) => return Err(if expected == '-' {
278 Mismatch{location, expected, found: c}
279 } else {
280 Mismatch{location: prev_location, expected, found: '-'}
281 })
282 }
283 }
284
285 match label.push(c) {
286 Ok(None) => {},
287 Err(error) => return Err(LabelError{error, location}),
288 Ok(Some(expected)) => return Err(if expected == c {
289 Mismatch{location, expected: '-', found: c}
290 } else {
291 Mismatch{location, expected, found: c}
292 }),
293 }
294 },
295 EatEnd{end, expected} => (end, expected),
296 };
297
298 loop {
299 match stream.next() {
300 Some(Err(e)) => return Ok(Some((EatEnd{end, expected}, e))),
301 None => return Err(MissingExpected(expected)),
302 Some(Ok((location, found))) => if found != expected {
303 return Err(Mismatch{found, location, expected: expected})
304 } else if let Some(e) = end.next() {
305 expected = e;
306 } else {
307 return Ok(None)
308 },
309 }
310 }
311 }
312}
313
314#[cfg(test)]
315mod tests {
316 use super::{BoundaryType, BoundaryParser, LabelMatcher};
317 #[cfg(not(feature = "std"))]
318 use super::DiscardLabel;
319
320 #[test]
321 fn test_parse_boundary() {
322 #[cfg(feature = "std")]
323 fn helper(b: BoundaryType, input: &str, _label: &str) {
324 #[cfg(feature = "std")]
325 let mut label_buf = String::new();
326
327 {
328 let mut parser = BoundaryParser::from_chars(b, input.chars().enumerate(), &mut label_buf);
329 assert_eq!(parser.next(), None);
330 assert_eq!(parser.complete(), Ok(()));
331 }
332
333 assert_eq!(_label, label_buf.as_str());
334 }
335 #[cfg(not(feature = "std"))]
336 fn helper(b: BoundaryType, input: &str, _label: &str) {
337 let mut parser = BoundaryParser::from_chars(b, input.chars().enumerate(), DiscardLabel);
338 assert_eq!(parser.next(), None);
339 assert_eq!(parser.complete(), Ok(()));
340 }
341
342 const BEGIN_PRIVATE: &'static str = "-----BEGIN RSA PRIVATE KEY-----";
343 const BEGIN_INT_CERT: &'static str = "-----BEGIN INTERMEDIATE CERT-----";
344 const BEGIN_CERT: &'static str = "-----BEGIN CERTIFICATE-----";
345 const BEGIN_COMPLEX: &'static str = "\t\r -----BEGIN \u{211D}-\u{212D}-----";
346
347 const END_PRIVATE: &'static str = "----END RSA PRIVATE KEY-----";
349 const END_INT_CERT: &'static str = "----END INTERMEDIATE CERT-----";
350 const END_CERT: &'static str = "----END CERTIFICATE-----";
351 const END_COMPLEX: &'static str = "----END \u{211D}-\u{212D}-----";
352
353 helper(BoundaryType::Begin, BEGIN_CERT, "CERTIFICATE");
354 helper(BoundaryType::Begin, BEGIN_INT_CERT, "INTERMEDIATE CERT");
355 helper(BoundaryType::Begin, BEGIN_PRIVATE, "RSA PRIVATE KEY");
356 helper(BoundaryType::Begin, BEGIN_COMPLEX, "\u{211D}-\u{212D}");
357
358 helper(BoundaryType::End, END_CERT, "CERTIFICATE");
359 helper(BoundaryType::End, END_INT_CERT, "INTERMEDIATE CERT");
360 helper(BoundaryType::End, END_PRIVATE, "RSA PRIVATE KEY");
361 helper(BoundaryType::End, END_COMPLEX, "\u{211D}-\u{212D}");
362 }
363
364 #[test]
365 fn test_verify_boundary() {
366 fn helper(b: BoundaryType, input: &str, label: &str) {
367 let mut verifier = BoundaryParser::from_chars(b, input.chars().enumerate(), LabelMatcher(label.chars()));
368 assert_eq!(verifier.next(), None);
369 assert_eq!(verifier.complete(), Ok(()));
370 }
371
372 const BEGIN_PRIVATE: &'static str = "-----BEGIN RSA PRIVATE KEY-----";
373 const BEGIN_INT_CERT: &'static str = "-----BEGIN INTERMEDIATE CERT-----";
374 const BEGIN_CERT: &'static str = "-----BEGIN CERTIFICATE-----";
375 const BEGIN_COMPLEX: &'static str = "\t\r -----BEGIN \u{211D}-\u{212D}-----";
376
377 const END_PRIVATE: &'static str = "----END RSA PRIVATE KEY-----";
379 const END_INT_CERT: &'static str = "----END INTERMEDIATE CERT-----";
380 const END_CERT: &'static str = "----END CERTIFICATE-----";
381 const END_COMPLEX: &'static str = "----END \u{211D}-\u{212D}-----";
382
383 helper(BoundaryType::Begin, BEGIN_CERT, "CERTIFICATE");
384 helper(BoundaryType::Begin, BEGIN_INT_CERT, "INTERMEDIATE CERT");
385 helper(BoundaryType::Begin, BEGIN_PRIVATE, "RSA PRIVATE KEY");
386 helper(BoundaryType::Begin, BEGIN_COMPLEX, "\u{211D}-\u{212D}");
387
388 helper(BoundaryType::End, END_CERT, "CERTIFICATE");
389 helper(BoundaryType::End, END_INT_CERT, "INTERMEDIATE CERT");
390 helper(BoundaryType::End, END_PRIVATE, "RSA PRIVATE KEY");
391 helper(BoundaryType::End, END_COMPLEX, "\u{211D}-\u{212D}");
392 }
393}