use core::ops::{Generator};
use boundary::{BoundaryType, EncapsulationBoundaryError, Label};
use body::{BodyError, Bytes, get_6_bits};
use {Void, map_chars, is_whitespace};
pub fn parse_boundary_chars<Loc, Lbl, S>(b: BoundaryType, stream: S, label: Lbl) -> impl Generator<
Yield=Void,
Return=Result<(), EncapsulationBoundaryError<Loc, Lbl::LabelError>>,
>
where Lbl: Label,
S: Iterator<Item = (Loc, char)> {
parse_boundary(b, stream.map(map_chars), label)
}
pub fn parse_boundary<Loc, Lbl, E, S>(b: BoundaryType, mut stream: S, mut label: Lbl) -> impl Generator<
Yield=E,
Return=Result<(), EncapsulationBoundaryError<Loc, Lbl::LabelError>>,
>
where Lbl: Label,
S: Iterator<Item = Result<(Loc, char), E>> {
move ||{
let key = match b {
BoundaryType::Begin => {
loop {
use self::EncapsulationBoundaryError::*;
let v = stream.by_ref()
.skip_while(|c| c.as_ref().ok().map_or(false, is_whitespace)).next();
match v {
Some(Err(e)) => yield e,
None => return Err(MissingExpected('-')),
Some(Ok((location, found))) => if found != '-' {
return Err(Mismatch {expected: '-', found, location})
} else {
break
}
}
}
"----BEGIN "
},
BoundaryType::End => "----END ",
};
for expected in key.chars() {
use self::EncapsulationBoundaryError::*;
loop {
let v = stream.next();
match v {
Some(Err(e)) => yield e,
None => return Err(MissingExpected(expected)),
Some(Ok((location, found))) => if expected != found {
return Err(Mismatch {expected, found, location})
} else {
break
}
}
}
}
let mut prev_dash = None;
loop {
use self::EncapsulationBoundaryError::*;
let v = stream.next();
let (location, c) = match v {
Some(Err(e)) => {
yield e;
continue
},
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;
}
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}
}),
}
}
for expected in "---".chars() {
use self::EncapsulationBoundaryError::*;
loop {
let v = stream.next();
match v {
Some(Err(e)) => yield e,
None => return Err(MissingExpected(expected)),
Some(Ok((location, found))) => if expected != found {
return Err(Mismatch {expected, found, location})
} else {
break
}
}
}
}
Ok(())
}
}
pub fn parse_body_chunked_chars<Location, S>(stream: S) -> impl Generator<
Yield=Result<Bytes, BodyError<Location, Void>>,
Return=(),
>
where S: Iterator<Item = (Location, char)> {
parse_body_chunked(stream.map(map_chars))
}
pub fn parse_body_chunked<Location, E, S>(mut stream: S) -> impl Generator<
Yield=Result<Bytes, BodyError<Location, E>>,
Return=(),
>
where S: Iterator<Item = Result<(Location, char), E>> {
move ||{
loop {
let a = loop {
let v = get_6_bits(&mut stream);
match v {
Err(e) => yield Err(e),
Ok(None) => return,
Ok(Some(v)) => break v << 2,
}
};
let (a, b) = loop {
let v = get_6_bits(&mut stream);
match v {
Err(e) => yield Err(e),
Ok(None) => {
yield Ok(Bytes::One([a]));
return
},
Ok(Some(v)) => break (a | (v >> 4), (v & 0b1111) << 4),
}
};
let (b, c) = loop {
let v = get_6_bits(&mut stream);
match v {
Err(e) => yield Err(e),
Ok(None) => {
yield Ok(Bytes::Two([a, b]));
return
},
Ok(Some(v)) => break (b | (v >> 2), (v & 0b11) << 6),
}
};
let c = loop {
let v = get_6_bits(&mut stream);
match v {
Err(e) => yield Err(e),
Ok(None) => {
yield Ok(Bytes::Three([a, b, c]));
return
},
Ok(Some(v)) => break c | v,
}
};
yield Ok(Bytes::Three([a, b, c]));
}
}
}
pub fn parse_body_single_chars<Location, S>(stream: S) -> impl Generator<
Yield=Result<u8, BodyError<Location, Void>>,
Return=(),
>
where S: Iterator<Item = (Location, char)> {
parse_body_single(stream.map(map_chars))
}
pub fn parse_body_single<Location, E, S>(mut stream: S) -> impl Generator<
Yield=Result<u8, BodyError<Location, E>>,
Return=(),
>
where S: Iterator<Item = Result<(Location, char), E>> {
move || {
loop {
let o = loop {
let v = get_6_bits(&mut stream);
match v {
Err(e) => yield Err(e),
Ok(None) => return,
Ok(Some(v)) => break v << 2,
}
};
let o = loop {
let v = get_6_bits(&mut stream);
match v {
Err(e) => yield Err(e),
Ok(None) => {
yield Ok(o);
return
},
Ok(Some(v)) => {
yield Ok(o | (v >> 4));
break (v & 0b1111) << 4;
},
}
};
let o = loop {
let v = get_6_bits(&mut stream);
match v {
Err(e) => yield Err(e),
Ok(None) => {
yield Ok(o);
return
},
Ok(Some(v)) => {
yield Ok(o | (v >> 2));
break (v & 0b11) << 6;
},
}
};
loop {
let v = get_6_bits(&mut stream);
match v {
Err(e) => yield Err(e),
Ok(None) => {
yield Ok(o);
return
},
Ok(Some(v)) =>{
yield Ok(o | v);
break;
},
}
}
}
}
}
#[cfg(test)]
mod tests {
use core::ops::{Generator, GeneratorState};
use boundary::{BoundaryType, LabelMatcher};
#[cfg(not(feature = "std"))]
use boundary::DiscardLabel;
use super::{parse_boundary_chars};
#[test]
fn test_parse_boundary() {
fn helper(b: BoundaryType, input: &str, _label: &str) {
#[cfg(feature = "std")]
let mut label_buf = String::new();
{
#[cfg(not(feature = "std"))]
let label_buf = DiscardLabel;
#[cfg(feature = "std")]
let label_buf = &mut label_buf;
let mut gen = parse_boundary_chars(b, input.chars().enumerate(), label_buf);
match gen.resume() {
GeneratorState::Yielded(_) => unreachable!(),
GeneratorState::Complete(r) => assert_eq!(r, Ok(())),
}
}
#[cfg(feature = "std")]
assert_eq!(_label, label_buf.as_str());
}
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 gen = parse_boundary_chars(b, input.chars().enumerate(), LabelMatcher(label.chars()));
match gen.resume() {
GeneratorState::Yielded(_) => unreachable!(),
GeneratorState::Complete(r) => assert_eq!(r, 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}");
}
}