http_box/util/
decode.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;
18
19use std::fmt;
20
21/// Decoding errors.
22pub enum DecodeError {
23    /// Invalid byte.
24    Byte(u8),
25
26    /// Invalid hex sequence.
27    HexSequence(u8)
28}
29
30impl DecodeError {
31    /// Format this for debug and display purposes.
32    fn format(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
33        match *self {
34            DecodeError::Byte(x) => {
35                write!(
36                    formatter,
37                    "<DecodeError::Byte: {}>",
38                    x
39                )
40            },
41            DecodeError::HexSequence(x) => {
42                write!(
43                    formatter,
44                    "<DecodeError::HexSequence: {}>",
45                    x
46                )
47            }
48        }
49    }
50}
51
52impl fmt::Debug for DecodeError {
53    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
54        self.format(formatter)
55    }
56}
57
58impl fmt::Display for DecodeError {
59    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
60        self.format(formatter)
61    }
62}
63
64// -------------------------------------------------------------------------------------------------
65
66/// Decode URL encoded data.
67///
68/// # Arguments
69///
70/// **`encoded`**
71///
72/// The encoded data.
73///
74/// # Returns
75///
76/// **`String`**
77///
78/// The decoded string.
79///
80/// # Errors
81///
82/// - [`DecodeError::Byte`](enum.DecodeError.html#variant.Byte)
83/// - [`DecodeError::HexSequence`](enum.DecodeError.html#variant.HexSequence)
84///
85/// # Examples
86///
87/// ```
88/// use http_box::util;
89///
90/// let string = match util::decode(b"fancy%20url%20encoded%20data") {
91///     Ok(string) => string,
92///     Err(_) => panic!()
93/// };
94///
95/// assert_eq!(string, "fancy url encoded data");
96/// ```
97pub fn decode(encoded: &[u8]) -> Result<String, DecodeError> {
98    macro_rules! submit {
99        ($string:expr, $slice:expr) => (unsafe {
100            $string.as_mut_vec().extend_from_slice($slice);
101        });
102    }
103
104    let mut context = ByteStream::new(encoded);
105    let mut string  = String::new();
106
107    loop {
108        bs_mark!(context);
109
110        collect_visible_7bit!(
111            context,
112
113            // stop on these bytes
114               context.byte == b'+'
115            || context.byte == b'%',
116
117            // on end-of-stream
118            {
119                if context.mark_index < context.stream_index {
120                    submit!(string, bs_slice!(context));
121                }
122
123                return Ok(string);
124            }
125        );
126
127        if bs_slice_length!(context) > 1 {
128            submit!(string, bs_slice_ignore!(context));
129        }
130
131        if context.byte == b'+' {
132            submit!(string, b" ");
133        } else if context.byte == b'%' {
134            if bs_has_bytes!(context, 2) {
135                submit!(string, &[
136                    collect_hex8!(context, DecodeError::HexSequence)
137                ]);
138            } else {
139                if bs_has_bytes!(context, 1) {
140                    bs_next!(context);
141                }
142
143                return Err(DecodeError::HexSequence(context.byte));
144            }
145        } else {
146            return Err(DecodeError::Byte(context.byte));
147        }
148    }
149}