1extern crate iron;
7extern crate bodyparser;
8extern crate url;
9extern crate plugin;
10
11use iron::prelude::*;
12use iron::typemap::Key;
13
14use url::form_urlencoded;
15use std::collections::HashMap;
16use std::collections::hash_map::Entry::*;
17use std::fmt;
18use std::error::Error as StdError;
19
20pub struct UrlEncodedQuery;
24
25pub struct UrlEncodedBody;
29
30#[derive(Debug)]
38pub enum UrlDecodingError{
39 BodyError(bodyparser::BodyError),
41 EmptyQuery
43}
44
45pub use UrlDecodingError::*;
46
47impl fmt::Display for UrlDecodingError {
48 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
49 self.description().fmt(f)
50 }
51}
52
53impl StdError for UrlDecodingError {
54 fn description(&self) -> &str {
55 match *self {
56 BodyError(ref err) => err.description(),
57 EmptyQuery => "Expected query, found empty string"
58 }
59 }
60
61 fn cause(&self) -> Option<&StdError> {
62 match *self {
63 BodyError(ref err) => Some(err),
64 _ => None
65 }
66 }
67}
68
69pub type QueryMap = HashMap<String, Vec<String>>;
71pub type QueryResult = Result<QueryMap, UrlDecodingError>;
73
74impl Key for UrlEncodedBody {
75 type Value = QueryMap;
76}
77impl Key for UrlEncodedQuery {
78 type Value = QueryMap;
79}
80
81impl<'a, 'b> plugin::Plugin<Request<'a, 'b>> for UrlEncodedQuery {
82 type Error = UrlDecodingError;
83
84 fn eval(req: &mut Request) -> QueryResult {
85 match req.url.query() {
86 Some(ref query) => create_param_hashmap(&query),
87 None => Err(UrlDecodingError::EmptyQuery)
88 }
89 }
90}
91
92impl<'a, 'b> plugin::Plugin<Request<'a, 'b>> for UrlEncodedBody {
93 type Error = UrlDecodingError;
94
95 fn eval(req: &mut Request) -> QueryResult {
96 req.get::<bodyparser::Raw>()
97 .map(|x| x.unwrap_or("".to_string()))
98 .map_err(|e| UrlDecodingError::BodyError(e))
99 .and_then(|x| create_param_hashmap(&x))
100 }
101}
102
103fn create_param_hashmap(data: &str) -> QueryResult {
105 match data {
106 "" => Err(UrlDecodingError::EmptyQuery),
107 _ => Ok(combine_duplicates(form_urlencoded::parse(data.as_bytes()).into_owned()))
108 }
109}
110
111fn combine_duplicates<I: Iterator<Item=(String, String)>>(collection: I) -> QueryMap {
113 let mut deduplicated: QueryMap = HashMap::new();
114
115 for (k, v) in collection {
116 match deduplicated.entry(k) {
117 Occupied(entry) => { entry.into_mut().push(v); },
119
120 Vacant(entry) => { entry.insert(vec![v]); },
122 };
123 }
124
125 deduplicated
126}
127
128#[test]
129fn test_combine_duplicates() {
130 let my_vec = vec![("band".to_string(), "arctic monkeys".to_string()),
131 ("band".to_string(), "temper trap".to_string()),
132 ("color".to_string(),"green".to_string())];
133 let answer = combine_duplicates(my_vec.into_iter());
134 let mut control = HashMap::new();
135 control.insert("band".to_string(),
136 vec!["arctic monkeys".to_string(), "temper trap".to_string()]);
137 control.insert("color".to_string(), vec!["green".to_string()]);
138 assert_eq!(answer, control);
139}