use core::str::Chars;
use core::iter::{Map, once};
use {Void, map_chars, is_whitespace};
#[derive(Debug, PartialEq)]
pub enum EncapsulationBoundaryError<Location, LabelError> {
MissingExpected(char),
Mismatch {
location: Location,
expected: char,
found: char,
},
LabelError{
location: Location,
error: LabelError
},
}
pub enum BoundaryType {
Begin,
End,
}
pub trait Label {
type LabelError;
fn push(&mut self, found: char) -> Result<Option<char>, Self::LabelError>;
fn complete(&mut self) -> Result<Option<char>, Self::LabelError> {
Ok(None)
}
}
pub struct DiscardLabel;
pub struct LabelFn<F>(pub F);
pub struct LabelMatcher<I>(pub I);
impl<'a, T: 'a+Extend<char>> Label for &'a mut T {
type LabelError = Void;
fn push(&mut self, found: char) -> Result<Option<char>, Self::LabelError> {
self.extend(once(found));
Ok(None)
}
}
impl Label for DiscardLabel {
type LabelError = Void;
fn push(&mut self, _: char) -> Result<Option<char>, Self::LabelError> {
Ok(None)
}
}
impl <T, F> Label for LabelFn<F>
where F: FnMut(char) -> Result<Option<char>, T> {
type LabelError = T;
fn push(&mut self, found: char) -> Result<Option<char>, T> {
self.0(found)
}
}
impl<T: Iterator<Item=char>> Label for LabelMatcher<T> {
type LabelError = Void;
fn push(&mut self, found: char) -> Result<Option<char>, Self::LabelError> {
Ok(match self.0.next() {
Some(expected) => if expected == found {
None
} else {
Some(expected)
},
None => Some(found),
})
}
fn complete(&mut self) -> Result<Option<char>, Self::LabelError> {
Ok(self.0.next())
}
}
pub struct BoundaryParser<Loc, Lbl: Label, S> {
stream: S,
state: Option<BoundaryParserState<Loc, Lbl>>,
result: Result<(),EncapsulationBoundaryError<Loc, Lbl::LabelError>>,
}
enum BoundaryParserState<Loc, Lbl> {
EatFirst{
label: Lbl,
b: BoundaryType,
},
NotEatFirst(BoundaryParserState2<Loc, Lbl>),
}
enum BoundaryParserState2<Loc, Lbl> {
EatKey{
label: Lbl,
key: Chars<'static>,
expected: char,
},
NotEatKey(BoundaryParserState3<Loc, Lbl>)
}
enum BoundaryParserState3<Loc, Lbl> {
EatLabel{
label: Lbl,
prev_dash: Option<Loc>,
},
EatEnd{
end: Chars<'static>,
expected: char,
},
}
impl<Loc, Lbl, E, S> BoundaryParser<Loc, Lbl, S>
where Lbl: Label,
S: Iterator<Item = Result<(Loc, char), E>>
{
pub fn new(b: BoundaryType, stream: S, label: Lbl) -> Self {
BoundaryParser{
stream, state: Some(BoundaryParserState::EatFirst{label, b}), result: Ok(()),
}
}
pub fn complete(self) -> Result<(), EncapsulationBoundaryError<Loc, Lbl::LabelError>> {
self.result
}
}
impl<Loc, Lbl, S> BoundaryParser<Loc, Lbl, Map<S, fn((Loc, char)) -> Result<(Loc, char), Void>>>
where Lbl: Label,
S: Iterator<Item = (Loc, char)>
{
pub fn from_chars(b: BoundaryType, stream: S, label: Lbl) -> Self {
Self::new(b, stream.map(map_chars), label)
}
}
impl<Loc, Lbl, E, S> Iterator for BoundaryParser<Loc, Lbl, S>
where Lbl: Label,
S: Iterator<Item = Result<(Loc, char), E>>
{
type Item = E;
fn next(&mut self) -> Option<E> {
match self.state.take().unwrap().process(&mut self.stream) {
Err(e) => {
self.result = Err(e);
None
},
Ok(None) => None,
Ok(Some((s, e))) => {
self.state = Some(s);
Some(e)
},
}
}
}
impl<Loc, Lbl: Label> BoundaryParserState<Loc, Lbl> {
fn process<'a, E: 'a>(self, stream: &'a mut Iterator<Item = Result<(Loc, char), E>>) -> Result<Option<(Self, E)>, EncapsulationBoundaryError<Loc, Lbl::LabelError>> {
use self::EncapsulationBoundaryError::*;
use self::BoundaryParserState::*;
use self::BoundaryParserState2::*;
let v = match self {
EatFirst{label, b} => {
let key = match b {
BoundaryType::Begin => {
match stream.skip_while(|c| c.as_ref().ok().map_or(false, is_whitespace)).next() {
Some(Err(e)) => return Ok(Some((EatFirst{label, b}, e))),
None => return Err(MissingExpected('-')),
Some(Ok((location, found))) => if found != '-' {
return Err(Mismatch{found, location, expected: '-'})
},
}
"---BEGIN "
},
BoundaryType::End => "---END ",
}.chars();
EatKey{label, key, expected: '-'}
},
NotEatFirst(v) => v,
};
v.process(stream).map(|v| v.map(|(v, e)| (NotEatFirst(v), e)))
}
}
impl<Loc, Lbl: Label> BoundaryParserState2<Loc, Lbl> {
fn process<'a, E: 'a>(self, stream: &'a mut Iterator<Item = Result<(Loc, char), E>>) -> Result<Option<(Self, E)>, EncapsulationBoundaryError<Loc, Lbl::LabelError>> {
use self::EncapsulationBoundaryError::*;
use self::BoundaryParserState2::*;
use self::BoundaryParserState3::*;
let v = match self {
EatKey{label, mut key, mut expected} => loop {
match stream.next() {
Some(Err(e)) => return Ok(Some((EatKey{label, key, expected}, e))),
None => return Err(MissingExpected(expected)),
Some(Ok((location, found))) => if found != expected {
return Err(Mismatch{found, location, expected})
} else if let Some(e) = key.next() {
expected = e;
} else {
break EatLabel{label, prev_dash: None}
},
}
},
NotEatKey(v) => v,
};
v.process(stream).map(|v| v.map(|(v, e)| (NotEatKey(v), e)))
}
}
impl<Loc, Lbl: Label> BoundaryParserState3<Loc, Lbl> {
fn process<'a, E: 'a>(self, stream: &'a mut Iterator<Item = Result<(Loc, char), E>>) -> Result<Option<(Self, E)>, EncapsulationBoundaryError<Loc, Lbl::LabelError>> {
use self::EncapsulationBoundaryError::*;
use self::BoundaryParserState3::*;
let (mut end, mut expected) = match self {
EatLabel{mut label, mut prev_dash} => loop {
use self::EncapsulationBoundaryError::*;
let v = stream.next();
let (location, c) = match v {
Some(Err(e)) => return Ok(Some((EatLabel{label, prev_dash}, e))),
None => return Err(MissingExpected('-')),
Some(Ok(c)) => c,
};
if c == '-' {
if prev_dash.is_none() {
prev_dash = Some(location);
continue;
}
match label.complete() {
Ok(None) => {},
Err(error) => return Err(LabelError{error, location}),
Ok(Some(expected)) => return Err(Mismatch{location, expected, found: c}),
}
break ("--".chars(), '-');
}
if let Some(prev_location) = prev_dash.take() {
match label.push('-') {
Ok(None) => {},
Err(error) => return Err(LabelError{error, location: prev_location}),
Ok(Some(expected)) => return Err(if expected == '-' {
Mismatch{location, expected, found: c}
} else {
Mismatch{location: prev_location, expected, found: '-'}
})
}
}
match label.push(c) {
Ok(None) => {},
Err(error) => return Err(LabelError{error, location}),
Ok(Some(expected)) => return Err(if expected == c {
Mismatch{location, expected: '-', found: c}
} else {
Mismatch{location, expected, found: c}
}),
}
},
EatEnd{end, expected} => (end, expected),
};
loop {
match stream.next() {
Some(Err(e)) => return Ok(Some((EatEnd{end, expected}, e))),
None => return Err(MissingExpected(expected)),
Some(Ok((location, found))) => if found != expected {
return Err(Mismatch{found, location, expected: expected})
} else if let Some(e) = end.next() {
expected = e;
} else {
return Ok(None)
},
}
}
}
}
#[cfg(test)]
mod tests {
use super::{BoundaryType, BoundaryParser, LabelMatcher};
#[cfg(not(feature = "std"))]
use super::DiscardLabel;
#[test]
fn test_parse_boundary() {
#[cfg(feature = "std")]
fn helper(b: BoundaryType, input: &str, _label: &str) {
#[cfg(feature = "std")]
let mut label_buf = String::new();
{
let mut parser = BoundaryParser::from_chars(b, input.chars().enumerate(), &mut label_buf);
assert_eq!(parser.next(), None);
assert_eq!(parser.complete(), Ok(()));
}
assert_eq!(_label, label_buf.as_str());
}
#[cfg(not(feature = "std"))]
fn helper(b: BoundaryType, input: &str, _label: &str) {
let mut parser = BoundaryParser::from_chars(b, input.chars().enumerate(), DiscardLabel);
assert_eq!(parser.next(), None);
assert_eq!(parser.complete(), Ok(()));
}
const BEGIN_PRIVATE: &'static str = "-----BEGIN RSA PRIVATE KEY-----";
const BEGIN_INT_CERT: &'static str = "-----BEGIN INTERMEDIATE CERT-----";
const BEGIN_CERT: &'static str = "-----BEGIN CERTIFICATE-----";
const BEGIN_COMPLEX: &'static str = "\t\r -----BEGIN \u{211D}-\u{212D}-----";
const END_PRIVATE: &'static str = "----END RSA PRIVATE KEY-----";
const END_INT_CERT: &'static str = "----END INTERMEDIATE CERT-----";
const END_CERT: &'static str = "----END CERTIFICATE-----";
const END_COMPLEX: &'static str = "----END \u{211D}-\u{212D}-----";
helper(BoundaryType::Begin, BEGIN_CERT, "CERTIFICATE");
helper(BoundaryType::Begin, BEGIN_INT_CERT, "INTERMEDIATE CERT");
helper(BoundaryType::Begin, BEGIN_PRIVATE, "RSA PRIVATE KEY");
helper(BoundaryType::Begin, BEGIN_COMPLEX, "\u{211D}-\u{212D}");
helper(BoundaryType::End, END_CERT, "CERTIFICATE");
helper(BoundaryType::End, END_INT_CERT, "INTERMEDIATE CERT");
helper(BoundaryType::End, END_PRIVATE, "RSA PRIVATE KEY");
helper(BoundaryType::End, END_COMPLEX, "\u{211D}-\u{212D}");
}
#[test]
fn test_verify_boundary() {
fn helper(b: BoundaryType, input: &str, label: &str) {
let mut verifier = BoundaryParser::from_chars(b, input.chars().enumerate(), LabelMatcher(label.chars()));
assert_eq!(verifier.next(), None);
assert_eq!(verifier.complete(), Ok(()));
}
const BEGIN_PRIVATE: &'static str = "-----BEGIN RSA PRIVATE KEY-----";
const BEGIN_INT_CERT: &'static str = "-----BEGIN INTERMEDIATE CERT-----";
const BEGIN_CERT: &'static str = "-----BEGIN CERTIFICATE-----";
const BEGIN_COMPLEX: &'static str = "\t\r -----BEGIN \u{211D}-\u{212D}-----";
const END_PRIVATE: &'static str = "----END RSA PRIVATE KEY-----";
const END_INT_CERT: &'static str = "----END INTERMEDIATE CERT-----";
const END_CERT: &'static str = "----END CERTIFICATE-----";
const END_COMPLEX: &'static str = "----END \u{211D}-\u{212D}-----";
helper(BoundaryType::Begin, BEGIN_CERT, "CERTIFICATE");
helper(BoundaryType::Begin, BEGIN_INT_CERT, "INTERMEDIATE CERT");
helper(BoundaryType::Begin, BEGIN_PRIVATE, "RSA PRIVATE KEY");
helper(BoundaryType::Begin, BEGIN_COMPLEX, "\u{211D}-\u{212D}");
helper(BoundaryType::End, END_CERT, "CERTIFICATE");
helper(BoundaryType::End, END_INT_CERT, "INTERMEDIATE CERT");
helper(BoundaryType::End, END_PRIVATE, "RSA PRIVATE KEY");
helper(BoundaryType::End, END_COMPLEX, "\u{211D}-\u{212D}");
}
}