Skip to content

Commit

Permalink
introduce HeaderValue type to replace use of String
Browse files Browse the repository at this point in the history
  • Loading branch information
yskopets committed Aug 10, 2020
1 parent aa67a50 commit 6004488
Showing 1 changed file with 261 additions and 0 deletions.
261 changes: 261 additions & 0 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use std::fmt;
use std::hash::{Hash, Hasher};

use crate::traits::*;

pub type NewRootContext = fn(context_id: u32) -> Box<dyn RootContext>;
Expand Down Expand Up @@ -77,3 +80,261 @@ pub enum PeerType {
}

pub type Bytes = Vec<u8>;

/// Represents an HTTP header value that is not necessarily a UTF-8 encoded string.
#[derive(Eq)]
pub struct HeaderValue {
inner: Result<String, Bytes>,
}

impl HeaderValue {
fn new(inner: Result<String, Bytes>) -> Self {
HeaderValue { inner }
}

pub fn into_vec(self) -> Vec<u8> {
match self.inner {
Ok(string) => string.into_bytes(),
Err(bytes) => bytes,
}
}

pub fn into_string_or_vec(self) -> Result<String, Vec<u8>> {
self.inner
}
}

impl From<Vec<u8>> for HeaderValue {
#[inline]
fn from(data: Vec<u8>) -> Self {
Self::new(match String::from_utf8(data) {
Ok(string) => Ok(string),
Err(err) => Err(err.into_bytes()),
})
}
}

impl From<&[u8]> for HeaderValue {
fn from(data: &[u8]) -> Self {
data.to_owned().into()
}
}

impl From<String> for HeaderValue {
#[inline]
fn from(string: String) -> Self {
Self::new(Ok(string))
}
}

impl From<&str> for HeaderValue {
fn from(data: &str) -> Self {
data.to_owned().into()
}
}

impl fmt::Display for HeaderValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.inner {
Ok(ref string) => fmt::Display::fmt(string, f),
Err(ref bytes) => fmt::Debug::fmt(bytes, f),
}
}
}

impl fmt::Debug for HeaderValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.inner {
Ok(ref string) => fmt::Debug::fmt(string, f),
Err(ref bytes) => fmt::Debug::fmt(bytes, f),
}
}
}

impl AsRef<[u8]> for HeaderValue {
#[inline]
fn as_ref(&self) -> &[u8] {
match self.inner {
Ok(ref string) => string.as_bytes(),
Err(ref bytes) => bytes.as_slice(),
}
}
}

impl PartialEq for HeaderValue {
#[inline]
fn eq(&self, other: &HeaderValue) -> bool {
self.inner == other.inner
}
}

impl PartialEq<String> for HeaderValue {
#[inline]
fn eq(&self, other: &String) -> bool {
self.as_ref() == other.as_bytes()
}
}

impl PartialEq<Vec<u8>> for HeaderValue {
#[inline]
fn eq(&self, other: &Vec<u8>) -> bool {
self.as_ref() == other.as_slice()
}
}

impl PartialEq<&str> for HeaderValue {
#[inline]
fn eq(&self, other: &&str) -> bool {
self.as_ref() == other.as_bytes()
}
}

impl PartialEq<&[u8]> for HeaderValue {
#[inline]
fn eq(&self, other: &&[u8]) -> bool {
self.as_ref() == *other
}
}

impl PartialEq<HeaderValue> for String {
#[inline]
fn eq(&self, other: &HeaderValue) -> bool {
*other == *self
}
}

impl PartialEq<HeaderValue> for Vec<u8> {
#[inline]
fn eq(&self, other: &HeaderValue) -> bool {
*other == *self
}
}

impl PartialEq<HeaderValue> for &str {
#[inline]
fn eq(&self, other: &HeaderValue) -> bool {
*other == *self
}
}

impl PartialEq<HeaderValue> for &[u8] {
#[inline]
fn eq(&self, other: &HeaderValue) -> bool {
*other == *self
}
}

impl Hash for HeaderValue {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
match self.inner {
Ok(ref string) => Hash::hash(string, state),
Err(ref bytes) => Hash::hash(bytes, state),
}
}
}

#[cfg(test)]
mod tests {
use super::*;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};

#[test]
fn test_header_value_display_string() {
let string: HeaderValue = String::from("utf-8 encoded string").into();

assert_eq!(format!("{}", string), "utf-8 encoded string");
}

#[test]
fn test_header_value_display_bytes() {
let bytes: HeaderValue = vec![144u8, 145u8, 146u8].into();

assert_eq!(format!("{}", bytes), "[144, 145, 146]");
}

#[test]
fn test_header_value_debug_string() {
let string: HeaderValue = String::from("utf-8 encoded string").into();

assert_eq!(format!("{:?}", string), "\"utf-8 encoded string\"");
}

#[test]
fn test_header_value_debug_bytes() {
let bytes: HeaderValue = vec![144u8, 145u8, 146u8].into();

assert_eq!(format!("{:?}", bytes), "[144, 145, 146]");
}

#[test]
fn test_header_value_as_ref() {
fn receive<T>(value: T)
where
T: AsRef<[u8]>,
{
value.as_ref();
}

let string: HeaderValue = String::from("utf-8 encoded string").into();
receive(string);

let bytes: HeaderValue = vec![144u8, 145u8, 146u8].into();
receive(bytes);
}

#[test]
fn test_header_value_eq_string() {
let string: HeaderValue = String::from("utf-8 encoded string").into();

assert_eq!(string, "utf-8 encoded string");
assert_eq!(string, b"utf-8 encoded string" as &[u8]);

assert_eq!("utf-8 encoded string", string);
assert_eq!(b"utf-8 encoded string" as &[u8], string);

assert_eq!(string, string);
}

#[test]
fn test_header_value_eq_bytes() {
let bytes: HeaderValue = vec![144u8, 145u8, 146u8].into();

assert_eq!(bytes, vec![144u8, 145u8, 146u8]);
assert_eq!(bytes, b"\x90\x91\x92" as &[u8]);

assert_eq!(vec![144u8, 145u8, 146u8], bytes);
assert_eq!(b"\x90\x91\x92" as &[u8], bytes);

assert_eq!(bytes, bytes);
}

fn hash<T: Hash>(t: &T) -> u64 {
let mut h = DefaultHasher::new();
t.hash(&mut h);
h.finish()
}

#[test]
fn test_header_value_hash_string() {
let string: HeaderValue = String::from("utf-8 encoded string").into();

assert_eq!(hash(&string), hash(&"utf-8 encoded string"));
assert_ne!(hash(&string), hash(&b"utf-8 encoded string"));

assert_eq!(hash(&"utf-8 encoded string"), hash(&string));
assert_ne!(hash(&b"utf-8 encoded string"), hash(&string));
}

#[test]
fn test_header_value_hash_bytes() {
let bytes: HeaderValue = vec![144u8, 145u8, 146u8].into();

assert_eq!(hash(&bytes), hash(&vec![144u8, 145u8, 146u8]));
assert_eq!(hash(&bytes), hash(&[144u8, 145u8, 146u8]));

assert_eq!(hash(&vec![144u8, 145u8, 146u8]), hash(&bytes));
assert_eq!(hash(&[144u8, 145u8, 146u8]), hash(&bytes));
}
}

0 comments on commit 6004488

Please sign in to comment.