1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
use pear::parser;
use pear::parsers::*;

use {Accept, QMediaType};
use parse::checkers::is_whitespace;
use parse::media_type::media_type;

type Input<'a> = ::parse::IndexedInput<'a, str>;
type Result<'a, T> = ::pear::Result<T, Input<'a>>;

#[parser]
fn weighted_media_type<'a>(input: &mut Input<'a>) -> Result<'a, QMediaType> {
    let media_type = media_type()?;
    let weight = match media_type.params().next() {
        Some(("q", value)) if value.len() <= 5 => match value.parse::<f32>().ok() {
            Some(q) if q > 1. => return Err(pear_error!("q value must be <= 1")),
            Some(q) if q < 0. => return Err(pear_error!("q value must be > 0")),
            Some(q) => Some(q),
            None => return Err(pear_error!("invalid media-type weight"))
        },
        _ => None
    };

    QMediaType(media_type, weight)
}

#[parser]
fn accept<'a>(input: &mut Input<'a>) -> Result<'a, Accept> {
    Accept(series(false, ',', is_whitespace, weighted_media_type)?)
}

pub fn parse_accept(input: &str) -> Result<Accept> {
    parse!(accept: &mut input.into())
}

#[cfg(test)]
mod test {
    use MediaType;
    use super::parse_accept;

    macro_rules! assert_parse {
        ($string:expr) => ({
            match parse_accept($string) {
                Ok(accept) => accept,
                Err(e) => panic!("{:?} failed to parse: {}", $string, e)
            }
        });
    }

    macro_rules! assert_parse_eq {
        ($string:expr, [$($mt:expr),*]) => ({
            let expected = vec![$($mt),*];
            let result = assert_parse!($string);
            for (i, wmt) in result.iter().enumerate() {
                assert_eq!(wmt.media_type(), &expected[i]);
            }
        });
    }

    #[test]
    fn check_does_parse() {
        assert_parse!("text/html");
        assert_parse!("*/*, a/b; q=1.0; v=1, application/something, a/b");
        assert_parse!("a/b, b/c");
        assert_parse!("text/*");
        assert_parse!("text/*; q=1");
        assert_parse!("text/*; q=1; level=2");
        assert_parse!("audio/*; q=0.2, audio/basic");
        assert_parse!("text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c");
        assert_parse!("text/*, text/html, text/html;level=1, */*");
        assert_parse!("text/*;q=0.3, text/html;q=0.7, text/html;level=1, \
               text/html;level=2;q=0.4, */*;q=0.5");
    }

    #[test]
    fn check_parse_eq() {
        assert_parse_eq!("text/html", [MediaType::HTML]);
        assert_parse_eq!("text/html, application/json",
                         [MediaType::HTML, MediaType::JSON]);
        assert_parse_eq!("text/html; charset=utf-8; v=1, application/json",
                         [MediaType::HTML, MediaType::JSON]);
        assert_parse_eq!("text/html, text/html; q=0.1, text/html; q=0.2",
                         [MediaType::HTML, MediaType::HTML, MediaType::HTML]);
    }
}