use std::ascii::AsciiExt;
use std::collections::HashMap;
use error::{Error, Result};
pub fn alpha(c: char) -> bool {
c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z'
}
pub fn digit(c: char) -> bool {
c >= '0' && c <= '9'
}
pub fn tchar(c: char) -> bool {
c == '!' || c == '#' || c == '$' || c == '%' || c == '&' || c == '\'' || c == '*' ||
c == '+' || c == '-' || c == '.' || c == '^' ||
c == '_' || c == '`' || c == '|' || c == '~' || digit(c) || alpha(c)
}
pub fn bcharsnospace(c: char) -> bool {
digit(c) || alpha(c) || c == '\'' || c == '(' || c == ')' || c == '+' ||
c == '_' || c == ',' || c == '-' || c == '.' || c == '/' || c == ':' || c == '=' || c == '?'
}
pub fn bchars(c: char) -> bool {
bcharsnospace(c) || c == ' '
}
pub fn token(s: &str) -> bool {
!s.is_empty() && s.chars().all(tchar)
}
pub fn boundary(s: &str) -> bool {
!s.is_empty() && s.len() <= 70 && s.chars().all(bchars) &&
bcharsnospace(s.chars().last().unwrap())
}
fn is_whitespace(c: u8) -> bool {
c == b' ' || c == b'\n' || c == b'\r' || c == b'\t'
}
fn is_undefined(sequence: &[u8], s: usize) -> bool {
sequence.len() <= s
}
pub type Bytes = Vec<u8>;
pub fn parse_type_portion(sequence: &[u8], s: &mut usize) -> Result<(Bytes, Bytes)> {
let mut type_ = Vec::new();
let mut subtype = Vec::new();
let mut t: u8 = 0;
loop {
if t > 127 || is_undefined(sequence, *s) {
return Err(Error::Invalid);
}
if sequence[*s] == b'/' {
break;
}
type_.push(sequence[*s].to_ascii_lowercase());
*s += 1;
t += 1;
}
*s += 1;
let mut u: u8 = 0;
loop {
if u > 127 {
return Err(Error::Invalid);
}
if is_undefined(sequence, *s) {
return Ok((type_, subtype));
}
if is_whitespace(sequence[*s]) || sequence[*s] == b';' {
break;
}
subtype.push(sequence[*s].to_ascii_lowercase());
*s += 1;
u += 1;
}
Ok((type_, subtype))
}
fn parse_value(sequence: &[u8], s: &mut usize) -> Bytes {
let mut value = Vec::new();
if is_undefined(sequence, *s) {
return value;
}
if sequence[*s] == b'"' {
*s += 1;
'M3: loop {
if is_undefined(sequence, *s) || sequence[*s] == b'"' {
if sequence[*s] == b'"' {
*s += 1
}
return value;
}
if sequence[*s] == b'\\' && !is_undefined(sequence, *s + 1) {
*s += 1;
}
value.push(sequence[*s]);
*s += 1;
}
} else {
'M4: loop {
if is_undefined(sequence, *s) || is_whitespace(sequence[*s]) || sequence[*s] == b';' {
return value;
}
value.push(sequence[*s]);
*s += 1;
}
}
}
fn parse_parameters(sequence: &[u8], s: &mut usize) -> Result<HashMap<Bytes, Bytes>> {
let mut parameters = HashMap::new();
'L: loop {
'M: loop {
if is_undefined(sequence, *s) || sequence[*s] == b';' {
break 'M;
}
if is_whitespace(sequence[*s]) {
*s += 1;
continue;
}
if sequence[*s] == b'"' {
*s += 1;
'N: loop {
if is_undefined(sequence, *s) || sequence[*s] == b'"' {
if sequence[*s] == b'"' {
*s += 1;
}
break 'N;
}
if sequence[*s] == b'\\' && !is_undefined(sequence, *s + 1) {
*s += 1
}
*s += 1;
}
} else {
'N2: loop {
if is_undefined(sequence, *s) || is_whitespace(sequence[*s]) ||
sequence[*s] == b';' {
break 'N2;
}
*s += 1;
}
}
}
if is_undefined(sequence, *s) {
return Ok(parameters);
}
*s += 1;
while is_whitespace(sequence[*s]) {
*s += 1;
}
let mut name = Vec::new();
let mut extra = Vec::new();
let mut p = 0;
'M2: loop {
name.extend(extra.iter());
loop {
if !(is_whitespace(sequence[*s]) || sequence[*s] == b'=') {
if p > 127 {
return Err(Error::Invalid);
}
if is_undefined(sequence, *s) {
if name != b"" && parameters.get(&name).is_none() {
parameters.insert(name, Vec::new());
}
return Ok(parameters);
}
name.push(sequence[*s].to_ascii_lowercase());
*s += 1;
p += 1;
} else {
break;
}
}
loop {
if is_whitespace(sequence[*s]) {
extra.push(sequence[*s]);
*s += 1;
p += 1;
} else {
break;
}
}
if sequence[*s] == b'=' {
break 'M2;
}
}
*s += 1;
while is_whitespace(sequence[*s]) {
*s += 1;
}
parameters.insert(name, parse_value(sequence, s));
}
}
pub fn parse_media_type(sequence: &[u8]) -> Result<(Bytes, Bytes, HashMap<Bytes, Bytes>)> {
if sequence.is_empty() {
return Err(Error::Invalid);
}
let mut s: usize = 0;
while is_whitespace(sequence[s]) {
s += 1;
}
let (type_, subtype) = try!(parse_type_portion(sequence, &mut s));
let parameters = try!(parse_parameters(sequence, &mut s));
Ok((type_, subtype, parameters))
}