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}