use byte::{ is_header_field, is_quoted_header_field, is_token };
use byte_slice::ByteStream;
use std::fmt;
pub enum FieldError {
Name(u8),
Value(u8)
}
impl FieldError {
fn format(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
match *self {
FieldError::Name(x) => {
write!(
formatter,
"<FieldError::Name: Invalid field name on byte {}>",
x
)
},
FieldError::Value(x) => {
write!(
formatter,
"<FieldError::Value: Invalid field value on byte {}>",
x
)
}
}
}
}
impl fmt::Debug for FieldError {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
self.format(formatter)
}
}
impl fmt::Display for FieldError {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
self.format(formatter)
}
}
pub struct FieldIterator<'a> {
context: ByteStream<'a>,
delimiter: u8,
name: Vec<u8>,
normalize: bool,
on_error: Box<FnMut(FieldError) + 'a>,
value: Vec<u8>
}
impl<'a> FieldIterator<'a> {
pub fn new(field: &'a [u8], delimiter: u8, normalize: bool) -> FieldIterator<'a> {
FieldIterator{
context: ByteStream::new(field),
delimiter: delimiter,
name: Vec::new(),
normalize: normalize,
on_error: Box::new(|_|{}),
value: Vec::new()
}
}
pub fn on_error<F>(&mut self, on_error: F) -> &mut Self
where F : FnMut(FieldError) + 'a {
self.on_error = Box::new(on_error);
self
}
}
impl<'a> Iterator for FieldIterator<'a> {
type Item = (String, Option<String>);
fn next(&mut self) -> Option<(String, Option<String>)> {
if bs_available!(self.context) == 0 {
return None;
}
self.name.clear();
self.value.clear();
loop {
consume_linear_space!(
self.context,
return None
);
bs_replay!(self.context);
bs_mark!(self.context);
collect_tokens!(
self.context,
self.context.byte == b'='
|| self.context.byte == self.delimiter
|| self.context.byte == b'/'
|| (self.normalize && self.context.byte > 0x40 && self.context.byte < 0x5B),
{
if bs_slice_length!(self.context) > 0 {
self.name.extend_from_slice(bs_slice!(self.context));
}
submit_name!(self);
}
);
self.name.extend_from_slice(bs_slice_ignore!(self.context));
match self.context.byte {
b'=' => {
consume_linear_space!(
self.context,
submit_name!(self)
);
if self.context.byte == b'"' {
loop {
bs_mark!(self.context);
collect_quoted_field!(
self.context,
submit_error!(self, FieldError::Value)
);
if self.context.byte == b'"' {
self.value.extend_from_slice(bs_slice_ignore!(self.context));
consume_linear_space!(
self.context,
submit_name_value!(self)
);
if bs_available!(self.context) == 0 {
submit_name_value!(self);
}
bs_next!(self.context);
if self.context.byte == self.delimiter {
submit_name_value!(self);
}
submit_error!(self, FieldError::Value);
} else if self.context.byte == b'\\' {
if bs_is_eos!(self.context) {
submit_error!(self, FieldError::Name);
}
self.value.extend_from_slice(bs_slice_ignore!(self.context));
bs_next!(self.context);
self.value.push(self.context.byte);
} else {
bs_jump!(self.context, bs_available!(self.context));
(*self.on_error)(FieldError::Value(self.context.byte));
return None;
}
}
} else {
bs_replay!(self.context);
bs_mark!(self.context);
collect_field!(
self.context,
self.context.byte == self.delimiter,
{
if bs_slice_length!(self.context) > 0 {
self.value.extend_from_slice(bs_slice!(self.context));
}
submit_name_value!(self);
}
);
if bs_slice_length!(self.context) == 0 {
submit_name!(self);
}
if self.context.byte == self.delimiter {
submit_name_value!(self.name, bs_slice_ignore!(self.context));
} else {
bs_jump!(self.context, bs_available!(self.context));
(*self.on_error)(FieldError::Value(self.context.byte));
return None;
}
}
},
b'/' => {
self.name.push(b'/');
},
byte if byte == self.delimiter => {
submit_name!(self);
},
byte if byte > 0x40 && byte < 0x5B => {
self.name.push(self.context.byte + 0x20);
},
_ => {
bs_jump!(self.context, bs_available!(self.context));
(*self.on_error)(FieldError::Name(self.context.byte));
return None;
}
}
}
}
}