Skip to content

Commit 2d818a3

Browse files
committed
introduce HeaderValue type to replace use of String
Signed-off-by: Yaroslav Skopets <[email protected]>
1 parent aa67a50 commit 2d818a3

File tree

1 file changed

+263
-0
lines changed

1 file changed

+263
-0
lines changed

src/types.rs

+263
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
use std::fmt;
16+
use std::hash::{Hash, Hasher};
17+
1518
use crate::traits::*;
1619

1720
pub type NewRootContext = fn(context_id: u32) -> Box<dyn RootContext>;
@@ -77,3 +80,263 @@ pub enum PeerType {
7780
}
7881

7982
pub type Bytes = Vec<u8>;
83+
84+
/// Represents an HTTP header value that is not necessarily a UTF-8 encoded string.
85+
#[derive(Eq)]
86+
pub struct HeaderValue {
87+
inner: Result<String, Bytes>,
88+
}
89+
90+
impl HeaderValue {
91+
fn new(inner: Result<String, Bytes>) -> Self {
92+
HeaderValue { inner }
93+
}
94+
95+
pub fn into_vec(self) -> Vec<u8> {
96+
match self.inner {
97+
Ok(string) => string.into_bytes(),
98+
Err(bytes) => bytes,
99+
}
100+
}
101+
102+
pub fn into_string_or_vec(self) -> Result<String, Vec<u8>> {
103+
self.inner
104+
}
105+
}
106+
107+
impl From<Vec<u8>> for HeaderValue {
108+
#[inline]
109+
fn from(data: Vec<u8>) -> Self {
110+
Self::new(match String::from_utf8(data) {
111+
Ok(string) => Ok(string),
112+
Err(err) => Err(err.into_bytes()),
113+
})
114+
}
115+
}
116+
117+
impl From<&[u8]> for HeaderValue {
118+
#[inline]
119+
fn from(data: &[u8]) -> Self {
120+
data.to_owned().into()
121+
}
122+
}
123+
124+
impl From<String> for HeaderValue {
125+
#[inline]
126+
fn from(string: String) -> Self {
127+
Self::new(Ok(string))
128+
}
129+
}
130+
131+
impl From<&str> for HeaderValue {
132+
#[inline]
133+
fn from(data: &str) -> Self {
134+
data.to_owned().into()
135+
}
136+
}
137+
138+
impl fmt::Display for HeaderValue {
139+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140+
match self.inner {
141+
Ok(ref string) => fmt::Display::fmt(string, f),
142+
Err(ref bytes) => fmt::Debug::fmt(bytes, f),
143+
}
144+
}
145+
}
146+
147+
impl fmt::Debug for HeaderValue {
148+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149+
match self.inner {
150+
Ok(ref string) => fmt::Debug::fmt(string, f),
151+
Err(ref bytes) => fmt::Debug::fmt(bytes, f),
152+
}
153+
}
154+
}
155+
156+
impl AsRef<[u8]> for HeaderValue {
157+
#[inline]
158+
fn as_ref(&self) -> &[u8] {
159+
match self.inner {
160+
Ok(ref string) => string.as_bytes(),
161+
Err(ref bytes) => bytes.as_slice(),
162+
}
163+
}
164+
}
165+
166+
impl PartialEq for HeaderValue {
167+
#[inline]
168+
fn eq(&self, other: &HeaderValue) -> bool {
169+
self.inner == other.inner
170+
}
171+
}
172+
173+
impl PartialEq<String> for HeaderValue {
174+
#[inline]
175+
fn eq(&self, other: &String) -> bool {
176+
self.as_ref() == other.as_bytes()
177+
}
178+
}
179+
180+
impl PartialEq<Vec<u8>> for HeaderValue {
181+
#[inline]
182+
fn eq(&self, other: &Vec<u8>) -> bool {
183+
self.as_ref() == other.as_slice()
184+
}
185+
}
186+
187+
impl PartialEq<&str> for HeaderValue {
188+
#[inline]
189+
fn eq(&self, other: &&str) -> bool {
190+
self.as_ref() == other.as_bytes()
191+
}
192+
}
193+
194+
impl PartialEq<&[u8]> for HeaderValue {
195+
#[inline]
196+
fn eq(&self, other: &&[u8]) -> bool {
197+
self.as_ref() == *other
198+
}
199+
}
200+
201+
impl PartialEq<HeaderValue> for String {
202+
#[inline]
203+
fn eq(&self, other: &HeaderValue) -> bool {
204+
*other == *self
205+
}
206+
}
207+
208+
impl PartialEq<HeaderValue> for Vec<u8> {
209+
#[inline]
210+
fn eq(&self, other: &HeaderValue) -> bool {
211+
*other == *self
212+
}
213+
}
214+
215+
impl PartialEq<HeaderValue> for &str {
216+
#[inline]
217+
fn eq(&self, other: &HeaderValue) -> bool {
218+
*other == *self
219+
}
220+
}
221+
222+
impl PartialEq<HeaderValue> for &[u8] {
223+
#[inline]
224+
fn eq(&self, other: &HeaderValue) -> bool {
225+
*other == *self
226+
}
227+
}
228+
229+
impl Hash for HeaderValue {
230+
#[inline]
231+
fn hash<H: Hasher>(&self, state: &mut H) {
232+
match self.inner {
233+
Ok(ref string) => Hash::hash(string, state),
234+
Err(ref bytes) => Hash::hash(bytes, state),
235+
}
236+
}
237+
}
238+
239+
#[cfg(test)]
240+
mod tests {
241+
use super::*;
242+
use std::collections::hash_map::DefaultHasher;
243+
use std::hash::{Hash, Hasher};
244+
245+
#[test]
246+
fn test_header_value_display_string() {
247+
let string: HeaderValue = String::from("utf-8 encoded string").into();
248+
249+
assert_eq!(format!("{}", string), "utf-8 encoded string");
250+
}
251+
252+
#[test]
253+
fn test_header_value_display_bytes() {
254+
let bytes: HeaderValue = vec![144u8, 145u8, 146u8].into();
255+
256+
assert_eq!(format!("{}", bytes), "[144, 145, 146]");
257+
}
258+
259+
#[test]
260+
fn test_header_value_debug_string() {
261+
let string: HeaderValue = String::from("utf-8 encoded string").into();
262+
263+
assert_eq!(format!("{:?}", string), "\"utf-8 encoded string\"");
264+
}
265+
266+
#[test]
267+
fn test_header_value_debug_bytes() {
268+
let bytes: HeaderValue = vec![144u8, 145u8, 146u8].into();
269+
270+
assert_eq!(format!("{:?}", bytes), "[144, 145, 146]");
271+
}
272+
273+
#[test]
274+
fn test_header_value_as_ref() {
275+
fn receive<T>(value: T)
276+
where
277+
T: AsRef<[u8]>,
278+
{
279+
value.as_ref();
280+
}
281+
282+
let string: HeaderValue = String::from("utf-8 encoded string").into();
283+
receive(string);
284+
285+
let bytes: HeaderValue = vec![144u8, 145u8, 146u8].into();
286+
receive(bytes);
287+
}
288+
289+
#[test]
290+
fn test_header_value_eq_string() {
291+
let string: HeaderValue = String::from("utf-8 encoded string").into();
292+
293+
assert_eq!(string, "utf-8 encoded string");
294+
assert_eq!(string, b"utf-8 encoded string" as &[u8]);
295+
296+
assert_eq!("utf-8 encoded string", string);
297+
assert_eq!(b"utf-8 encoded string" as &[u8], string);
298+
299+
assert_eq!(string, string);
300+
}
301+
302+
#[test]
303+
fn test_header_value_eq_bytes() {
304+
let bytes: HeaderValue = vec![144u8, 145u8, 146u8].into();
305+
306+
assert_eq!(bytes, vec![144u8, 145u8, 146u8]);
307+
assert_eq!(bytes, b"\x90\x91\x92" as &[u8]);
308+
309+
assert_eq!(vec![144u8, 145u8, 146u8], bytes);
310+
assert_eq!(b"\x90\x91\x92" as &[u8], bytes);
311+
312+
assert_eq!(bytes, bytes);
313+
}
314+
315+
fn hash<T: Hash>(t: &T) -> u64 {
316+
let mut h = DefaultHasher::new();
317+
t.hash(&mut h);
318+
h.finish()
319+
}
320+
321+
#[test]
322+
fn test_header_value_hash_string() {
323+
let string: HeaderValue = String::from("utf-8 encoded string").into();
324+
325+
assert_eq!(hash(&string), hash(&"utf-8 encoded string"));
326+
assert_ne!(hash(&string), hash(&b"utf-8 encoded string"));
327+
328+
assert_eq!(hash(&"utf-8 encoded string"), hash(&string));
329+
assert_ne!(hash(&b"utf-8 encoded string"), hash(&string));
330+
}
331+
332+
#[test]
333+
fn test_header_value_hash_bytes() {
334+
let bytes: HeaderValue = vec![144u8, 145u8, 146u8].into();
335+
336+
assert_eq!(hash(&bytes), hash(&vec![144u8, 145u8, 146u8]));
337+
assert_eq!(hash(&bytes), hash(&[144u8, 145u8, 146u8]));
338+
339+
assert_eq!(hash(&vec![144u8, 145u8, 146u8]), hash(&bytes));
340+
assert_eq!(hash(&[144u8, 145u8, 146u8]), hash(&bytes));
341+
}
342+
}

0 commit comments

Comments
 (0)