http_box/util/
query.rs

1// +-----------------------------------------------------------------------------------------------+
2// | Copyright 2016 Sean Kerr                                                                      |
3// |                                                                                               |
4// | Licensed under the Apache License, Version 2.0 (the "License");                               |
5// | you may not use this file except in compliance with the License.                              |
6// | You may obtain a copy of the License at                                                       |
7// |                                                                                               |
8// |  http://www.apache.org/licenses/LICENSE-2.0                                                   |
9// |                                                                                               |
10// | Unless required by applicable law or agreed to in writing, software                           |
11// | distributed under the License is distributed on an "AS IS" BASIS,                             |
12// | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.                      |
13// | See the License for the specific language governing permissions and                           |
14// | limitations under the License.                                                                |
15// +-----------------------------------------------------------------------------------------------+
16
17use byte_slice::ByteStream;
18use std::fmt;
19
20/// Query errors.
21pub enum QueryError {
22    /// Invalid query name.
23    Name(u8),
24
25    /// Invalid query value.
26    Value(u8)
27}
28
29impl QueryError {
30    /// Format this for debug and display purposes.
31    fn format(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
32        match *self {
33            QueryError::Name(x) => {
34                write!(
35                    formatter,
36                    "<QueryError::Name: {}>",
37                    x
38                )
39            },
40            QueryError::Value(x) => {
41                write!(
42                    formatter,
43                    "<QueryError::Value: {}>",
44                    x
45                )
46            }
47        }
48    }
49}
50
51impl fmt::Debug for QueryError {
52    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
53        self.format(formatter)
54    }
55}
56
57impl fmt::Display for QueryError {
58    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
59        self.format(formatter)
60    }
61}
62
63// -------------------------------------------------------------------------------------------------
64
65/// Query iterator.
66///
67/// This allows you to iterate over a query string to retrieve `(name, value)` pairs.
68///
69/// # Errors
70///
71/// - [`QueryError::Name`](enum.QueryError.html#variant.Name)
72/// - [`QueryError::Value`](enum.QueryError.html#variant.Value)
73///
74/// ```rust
75/// extern crate http_box;
76///
77/// use http_box::util::QueryIterator;
78///
79/// fn main() {
80///     let query = b"field1=value1&field2=value2&field3";
81///
82///     for (n, (name, value)) in QueryIterator::new(query).enumerate() {
83///         if n == 0 {
84///             assert_eq!(
85///                 name,
86///                 "field1"
87///             );
88///
89///             assert_eq!(
90///                 value.unwrap(),
91///                 "value1"
92///             );
93///         } else if n == 1 {
94///             assert_eq!(
95///                 name,
96///                 "field2"
97///             );
98///
99///             assert_eq!(
100///                 value.unwrap(),
101///                 "value2"
102///             );
103///         } else if n == 2 {
104///             assert_eq!(
105///                 name,
106///                 "field3"
107///             );
108///
109///             assert_eq!(
110///                 value,
111///                 None
112///             );
113///         }
114///     }
115/// }
116/// ```
117pub struct QueryIterator<'a> {
118    context:  ByteStream<'a>,
119    name:     Vec<u8>,
120    on_error: Box<FnMut(QueryError) + 'a>,
121    value:    Vec<u8>
122}
123
124impl<'a> QueryIterator<'a> {
125    /// Create a new `QueryIterator`.
126    ///
127    /// # Arguments
128    ///
129    /// **`query`**
130    ///
131    /// The query string.
132    pub fn new(query: &'a [u8]) -> QueryIterator<'a> {
133        QueryIterator{
134            context:  ByteStream::new(query),
135            name:     Vec::new(),
136            on_error: Box::new(|_|{}),
137            value:    Vec::new()
138        }
139    }
140
141    /// Set the on error callback.
142    ///
143    /// # Arguments
144    ///
145    /// **`on_error`**
146    ///
147    /// The callback.
148    pub fn on_error<F>(&mut self, on_error: F) -> &mut Self
149    where F : FnMut(QueryError) + 'a {
150        self.on_error = Box::new(on_error);
151        self
152    }
153}
154
155impl<'a> Iterator for QueryIterator<'a> {
156    type Item = (String, Option<String>);
157
158    fn next(&mut self) -> Option<(String, Option<String>)> {
159        if bs_available!(self.context) == 0 {
160            return None;
161        }
162
163        self.name.clear();
164        self.value.clear();
165
166        loop {
167            // field loop
168            loop {
169                bs_mark!(self.context);
170
171                collect_visible_7bit!(
172                    self.context,
173
174                    // stop on these bytes
175                       self.context.byte == b'%'
176                    || self.context.byte == b'+'
177                    || self.context.byte == b'='
178                    || self.context.byte == b'&'
179                    || self.context.byte == b';',
180
181                    // on end-of-stream
182                    {
183                        if bs_slice_length!(self.context) > 0 {
184                            self.name.extend_from_slice(bs_slice!(self.context));
185                        }
186
187                        submit_name!(self);
188                    }
189                );
190
191                if bs_slice_length!(self.context) > 1 {
192                    self.name.extend_from_slice(bs_slice_ignore!(self.context));
193                }
194
195                match self.context.byte {
196                    b'%' => {
197                        if bs_has_bytes!(self.context, 2) {
198                            self.name.push(collect_hex8_iter!(
199                                self,
200                                self.context,
201                                QueryError::Name
202                            ));
203                        } else if bs_has_bytes!(self.context, 1) {
204                            bs_next!(self.context);
205                        }
206
207                        submit_error!(self, QueryError::Name);
208                    },
209                    b'+' => {
210                        self.name.push(b' ');
211                    },
212                    b'=' => {
213                        if self.context.stream_index == 1 {
214                            // first byte cannot be an equal sign
215                            submit_error!(self, QueryError::Name);
216                        }
217
218                        break;
219                    },
220                    _ if self.context.stream_index == 1 => {
221                        // first byte cannot be a delimiter
222                        submit_error!(self, QueryError::Name);
223                    },
224                      b'&'
225                    | b';' => {
226                        // name without a value
227                        submit_name!(self);
228                    },
229                    _ => {
230                        bs_jump!(self.context, bs_available!(self.context));
231
232                        (*self.on_error)(QueryError::Name(self.context.byte));
233
234                        return None;
235                    }
236                }
237            }
238
239            // value loop
240            loop {
241                bs_mark!(self.context);
242
243                collect_visible_7bit!(
244                    self.context,
245
246                    // stop on these bytes
247                       self.context.byte == b'%'
248                    || self.context.byte == b'+'
249                    || self.context.byte == b'&'
250                    || self.context.byte == b';',
251
252                    // on end-of-stream
253                    {
254                        if bs_slice_length!(self.context) > 0 {
255                            self.value.extend_from_slice(bs_slice!(self.context));
256                        }
257
258                        submit_name_value!(self);
259                    }
260                );
261
262                if bs_slice_length!(self.context) > 1 {
263                    self.value.extend_from_slice(bs_slice_ignore!(self.context));
264                }
265
266                match self.context.byte {
267                    b'%' => {
268                        if bs_has_bytes!(self.context, 2) {
269                            self.value.push(collect_hex8_iter!(
270                                self,
271                                self.context,
272                                QueryError::Value
273                            ));
274                        } else {
275                            if bs_has_bytes!(self.context, 1) {
276                                bs_next!(self.context);
277                            }
278
279                            submit_error!(self, QueryError::Value);
280                        }
281                    },
282                    b'+' => {
283                        self.value.push(b' ');
284                    },
285                      b'&'
286                    | b';' => {
287                        // name with a value
288                        submit_name_value!(self);
289                    },
290                    _ => {
291                        bs_jump!(self.context, bs_available!(self.context));
292
293                        (*self.on_error)(QueryError::Value(self.context.byte));
294
295                        return None;
296                    }
297                }
298            }
299        }
300    }
301}