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}