| use crate::bytes_serializable::BytesSerializable; |
| use crate::error::IggyError; |
| use bytes::{BufMut, Bytes, BytesMut}; |
| use serde::{Deserialize, Serialize}; |
| use serde_with::base64::Base64; |
| use serde_with::serde_as; |
| use std::collections::HashMap; |
| use std::fmt::{Display, Formatter}; |
| use std::hash::{Hash, Hasher}; |
| use std::str::FromStr; |
| |
| /// Represents a header key with a unique name. The name is case-insensitive and wraps a string. |
| #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] |
| pub struct HeaderKey(String); |
| |
| impl HeaderKey { |
| pub fn new(key: &str) -> Result<Self, IggyError> { |
| if key.is_empty() || key.len() > 255 { |
| return Err(IggyError::InvalidHeaderKey); |
| } |
| |
| Ok(Self(key.to_lowercase().to_string())) |
| } |
| |
| pub fn as_str(&self) -> &str { |
| &self.0 |
| } |
| } |
| |
| impl Display for HeaderKey { |
| fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { |
| write!(f, "{}", self.as_str()) |
| } |
| } |
| |
| impl Hash for HeaderKey { |
| fn hash<H: Hasher>(&self, state: &mut H) { |
| self.0.hash(state); |
| } |
| } |
| |
| impl FromStr for HeaderKey { |
| type Err = IggyError; |
| fn from_str(s: &str) -> Result<Self, Self::Err> { |
| Self::new(s) |
| } |
| } |
| |
| impl TryFrom<&str> for HeaderKey { |
| type Error = IggyError; |
| fn try_from(value: &str) -> Result<Self, Self::Error> { |
| Self::new(value) |
| } |
| } |
| |
| /// Represents a header value of a specific kind. |
| /// It consists of the following fields: |
| /// - `kind`: the kind of the header value. |
| /// - `value`: the value of the header. |
| #[serde_as] |
| #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] |
| pub struct HeaderValue { |
| /// The kind of the header value. |
| pub kind: HeaderKind, |
| /// The binary value of the header payload. |
| #[serde_as(as = "Base64")] |
| pub value: Bytes, |
| } |
| |
| /// Represents the kind of a header value. |
| #[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Hash)] |
| #[serde(rename_all = "snake_case")] |
| pub enum HeaderKind { |
| Raw, |
| String, |
| Bool, |
| Int8, |
| Int16, |
| Int32, |
| Int64, |
| Int128, |
| Uint8, |
| Uint16, |
| Uint32, |
| Uint64, |
| Uint128, |
| Float32, |
| Float64, |
| } |
| |
| impl HeaderKind { |
| /// Returns the code of the header kind. |
| pub fn as_code(&self) -> u8 { |
| match self { |
| HeaderKind::Raw => 1, |
| HeaderKind::String => 2, |
| HeaderKind::Bool => 3, |
| HeaderKind::Int8 => 4, |
| HeaderKind::Int16 => 5, |
| HeaderKind::Int32 => 6, |
| HeaderKind::Int64 => 7, |
| HeaderKind::Int128 => 8, |
| HeaderKind::Uint8 => 9, |
| HeaderKind::Uint16 => 10, |
| HeaderKind::Uint32 => 11, |
| HeaderKind::Uint64 => 12, |
| HeaderKind::Uint128 => 13, |
| HeaderKind::Float32 => 14, |
| HeaderKind::Float64 => 15, |
| } |
| } |
| |
| /// Returns the header kind from the code. |
| pub fn from_code(code: u8) -> Result<Self, IggyError> { |
| match code { |
| 1 => Ok(HeaderKind::Raw), |
| 2 => Ok(HeaderKind::String), |
| 3 => Ok(HeaderKind::Bool), |
| 4 => Ok(HeaderKind::Int8), |
| 5 => Ok(HeaderKind::Int16), |
| 6 => Ok(HeaderKind::Int32), |
| 7 => Ok(HeaderKind::Int64), |
| 8 => Ok(HeaderKind::Int128), |
| 9 => Ok(HeaderKind::Uint8), |
| 10 => Ok(HeaderKind::Uint16), |
| 11 => Ok(HeaderKind::Uint32), |
| 12 => Ok(HeaderKind::Uint64), |
| 13 => Ok(HeaderKind::Uint128), |
| 14 => Ok(HeaderKind::Float32), |
| 15 => Ok(HeaderKind::Float64), |
| _ => Err(IggyError::InvalidCommand), |
| } |
| } |
| } |
| |
| impl FromStr for HeaderKind { |
| type Err = IggyError; |
| fn from_str(s: &str) -> Result<Self, Self::Err> { |
| match s { |
| "raw" => Ok(HeaderKind::Raw), |
| "string" => Ok(HeaderKind::String), |
| "bool" => Ok(HeaderKind::Bool), |
| "int8" => Ok(HeaderKind::Int8), |
| "int16" => Ok(HeaderKind::Int16), |
| "int32" => Ok(HeaderKind::Int32), |
| "int64" => Ok(HeaderKind::Int64), |
| "int128" => Ok(HeaderKind::Int128), |
| "uint8" => Ok(HeaderKind::Uint8), |
| "uint16" => Ok(HeaderKind::Uint16), |
| "uint32" => Ok(HeaderKind::Uint32), |
| "uint64" => Ok(HeaderKind::Uint64), |
| "uint128" => Ok(HeaderKind::Uint128), |
| "float32" => Ok(HeaderKind::Float32), |
| "float64" => Ok(HeaderKind::Float64), |
| _ => Err(IggyError::CannotParseHeaderKind(s.to_string())), |
| } |
| } |
| } |
| |
| impl Display for HeaderValue { |
| fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { |
| write!(f, "{}: ", self.kind)?; |
| write!(f, "{}", self.value_only_to_string()) |
| } |
| } |
| |
| impl Display for HeaderKind { |
| fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { |
| match *self { |
| HeaderKind::Raw => write!(f, "raw"), |
| HeaderKind::String => write!(f, "string"), |
| HeaderKind::Bool => write!(f, "bool"), |
| HeaderKind::Int8 => write!(f, "int8"), |
| HeaderKind::Int16 => write!(f, "int16"), |
| HeaderKind::Int32 => write!(f, "int32"), |
| HeaderKind::Int64 => write!(f, "int64"), |
| HeaderKind::Int128 => write!(f, "int128"), |
| HeaderKind::Uint8 => write!(f, "uint8"), |
| HeaderKind::Uint16 => write!(f, "uint16"), |
| HeaderKind::Uint32 => write!(f, "uint32"), |
| HeaderKind::Uint64 => write!(f, "uint64"), |
| HeaderKind::Uint128 => write!(f, "uint128"), |
| HeaderKind::Float32 => write!(f, "float32"), |
| HeaderKind::Float64 => write!(f, "float64"), |
| } |
| } |
| } |
| |
| impl FromStr for HeaderValue { |
| type Err = IggyError; |
| fn from_str(s: &str) -> Result<Self, Self::Err> { |
| Self::from(HeaderKind::String, s.as_bytes()) |
| } |
| } |
| |
| impl HeaderValue { |
| /// Creates a new header value from the specified kind and value. |
| /// The kind is parsed from the string representation. |
| /// The value is parsed from the string representation. |
| pub fn from_kind_str_and_value_str(kind: &str, value: &str) -> Result<Self, IggyError> { |
| let kind = HeaderKind::from_str(kind)?; |
| Self::from_kind_and_value_str(kind, value) |
| } |
| |
| /// Creates a new header value from the specified kind and value. |
| /// The value is parsed from the string representation. |
| pub fn from_kind_and_value_str(kind: HeaderKind, value: &str) -> Result<Self, IggyError> { |
| match kind { |
| HeaderKind::Raw => Self::from_raw(value.as_bytes()), |
| HeaderKind::String => Self::from_str(value), |
| HeaderKind::Bool => Self::from_bool(value.parse()?), |
| HeaderKind::Int8 => Self::from_int8(value.parse()?), |
| HeaderKind::Int16 => Self::from_int16(value.parse()?), |
| HeaderKind::Int32 => Self::from_int32(value.parse()?), |
| HeaderKind::Int64 => Self::from_int64(value.parse()?), |
| HeaderKind::Int128 => Self::from_int128(value.parse()?), |
| HeaderKind::Uint8 => Self::from_uint8(value.parse()?), |
| HeaderKind::Uint16 => Self::from_uint16(value.parse()?), |
| HeaderKind::Uint32 => Self::from_uint32(value.parse()?), |
| HeaderKind::Uint64 => Self::from_uint64(value.parse()?), |
| HeaderKind::Uint128 => Self::from_uint128(value.parse()?), |
| HeaderKind::Float32 => Self::from_float32(value.parse()?), |
| HeaderKind::Float64 => Self::from_float64(value.parse()?), |
| } |
| } |
| /// Creates a new header value from the specified raw bytes. |
| pub fn from_raw(value: &[u8]) -> Result<Self, IggyError> { |
| Self::from(HeaderKind::Raw, value) |
| } |
| |
| /// Returns the raw bytes of the header value. |
| pub fn as_raw(&self) -> Result<&[u8], IggyError> { |
| if self.kind != HeaderKind::Raw { |
| return Err(IggyError::InvalidHeaderValue); |
| } |
| |
| Ok(&self.value) |
| } |
| |
| /// Returns the string representation of the header value. |
| pub fn as_str(&self) -> Result<&str, IggyError> { |
| if self.kind != HeaderKind::String { |
| return Err(IggyError::InvalidHeaderValue); |
| } |
| |
| Ok(std::str::from_utf8(&self.value)?) |
| } |
| |
| /// Creates a new header value from the specified string. |
| pub fn from_bool(value: bool) -> Result<Self, IggyError> { |
| Self::from(HeaderKind::Bool, if value { &[1] } else { &[0] }) |
| } |
| |
| /// Returns the boolean representation of the header value. |
| pub fn as_bool(&self) -> Result<bool, IggyError> { |
| if self.kind != HeaderKind::Bool { |
| return Err(IggyError::InvalidHeaderValue); |
| } |
| |
| match self.value[0] { |
| 0 => Ok(false), |
| 1 => Ok(true), |
| _ => Err(IggyError::InvalidHeaderValue), |
| } |
| } |
| |
| /// Creates a new header value from the specified boolean. |
| pub fn from_int8(value: i8) -> Result<Self, IggyError> { |
| Self::from(HeaderKind::Int8, &value.to_le_bytes()) |
| } |
| |
| /// Returns the i8 representation of the header value. |
| pub fn as_int8(&self) -> Result<i8, IggyError> { |
| if self.kind != HeaderKind::Int8 { |
| return Err(IggyError::InvalidHeaderValue); |
| } |
| |
| let value = self.value.to_vec().try_into(); |
| if value.is_err() { |
| return Err(IggyError::InvalidHeaderValue); |
| } |
| |
| Ok(i8::from_le_bytes(value.unwrap())) |
| } |
| |
| /// Creates a new header value from the specified i8. |
| pub fn from_int16(value: i16) -> Result<Self, IggyError> { |
| Self::from(HeaderKind::Int16, &value.to_le_bytes()) |
| } |
| |
| /// Returns the i16 representation of the header value. |
| pub fn as_int16(&self) -> Result<i16, IggyError> { |
| if self.kind != HeaderKind::Int16 { |
| return Err(IggyError::InvalidHeaderValue); |
| } |
| |
| let value = self.value.to_vec().try_into(); |
| if value.is_err() { |
| return Err(IggyError::InvalidHeaderValue); |
| } |
| |
| Ok(i16::from_le_bytes(value.unwrap())) |
| } |
| |
| /// Creates a new header value from the specified i16. |
| pub fn from_int32(value: i32) -> Result<Self, IggyError> { |
| Self::from(HeaderKind::Int32, &value.to_le_bytes()) |
| } |
| |
| /// Returns the i32 representation of the header value. |
| pub fn as_int32(&self) -> Result<i32, IggyError> { |
| if self.kind != HeaderKind::Int32 { |
| return Err(IggyError::InvalidHeaderValue); |
| } |
| |
| let value = self.value.to_vec().try_into(); |
| if value.is_err() { |
| return Err(IggyError::InvalidHeaderValue); |
| } |
| |
| Ok(i32::from_le_bytes(value.unwrap())) |
| } |
| |
| /// Creates a new header value from the specified i32. |
| pub fn from_int64(value: i64) -> Result<Self, IggyError> { |
| Self::from(HeaderKind::Int64, &value.to_le_bytes()) |
| } |
| |
| /// Returns the i64 representation of the header value. |
| pub fn as_int64(&self) -> Result<i64, IggyError> { |
| if self.kind != HeaderKind::Int64 { |
| return Err(IggyError::InvalidHeaderValue); |
| } |
| |
| let value = self.value.to_vec().try_into(); |
| if value.is_err() { |
| return Err(IggyError::InvalidHeaderValue); |
| } |
| |
| Ok(i64::from_le_bytes(value.unwrap())) |
| } |
| |
| /// Creates a new header value from the specified i128. |
| pub fn from_int128(value: i128) -> Result<Self, IggyError> { |
| Self::from(HeaderKind::Int128, &value.to_le_bytes()) |
| } |
| |
| /// Returns the i128 representation of the header value. |
| pub fn as_int128(&self) -> Result<i128, IggyError> { |
| if self.kind != HeaderKind::Int128 { |
| return Err(IggyError::InvalidHeaderValue); |
| } |
| |
| let value = self.value.to_vec().try_into(); |
| if value.is_err() { |
| return Err(IggyError::InvalidHeaderValue); |
| } |
| |
| Ok(i128::from_le_bytes(value.unwrap())) |
| } |
| |
| /// Creates a new header value from the specified u8. |
| pub fn from_uint8(value: u8) -> Result<Self, IggyError> { |
| Self::from(HeaderKind::Uint8, &value.to_le_bytes()) |
| } |
| |
| /// Returns the u8 representation of the header value. |
| pub fn as_uint8(&self) -> Result<u8, IggyError> { |
| if self.kind != HeaderKind::Uint8 { |
| return Err(IggyError::InvalidHeaderValue); |
| } |
| |
| Ok(self.value[0]) |
| } |
| |
| /// Creates a new header value from the specified u16. |
| pub fn from_uint16(value: u16) -> Result<Self, IggyError> { |
| Self::from(HeaderKind::Uint16, &value.to_le_bytes()) |
| } |
| |
| /// Returns the u16 representation of the header value. |
| pub fn as_uint16(&self) -> Result<u16, IggyError> { |
| if self.kind != HeaderKind::Uint16 { |
| return Err(IggyError::InvalidHeaderValue); |
| } |
| |
| let value = self.value.to_vec().try_into(); |
| if value.is_err() { |
| return Err(IggyError::InvalidHeaderValue); |
| } |
| |
| Ok(u16::from_le_bytes(value.unwrap())) |
| } |
| |
| /// Creates a new header value from the specified u32. |
| pub fn from_uint32(value: u32) -> Result<Self, IggyError> { |
| Self::from(HeaderKind::Uint32, &value.to_le_bytes()) |
| } |
| |
| /// Returns the u32 representation of the header value. |
| pub fn as_uint32(&self) -> Result<u32, IggyError> { |
| if self.kind != HeaderKind::Uint32 { |
| return Err(IggyError::InvalidHeaderValue); |
| } |
| |
| let value = self.value.to_vec().try_into(); |
| if value.is_err() { |
| return Err(IggyError::InvalidHeaderValue); |
| } |
| |
| Ok(u32::from_le_bytes(value.unwrap())) |
| } |
| |
| /// Creates a new header value from the specified u64. |
| pub fn from_uint64(value: u64) -> Result<Self, IggyError> { |
| Self::from(HeaderKind::Uint64, &value.to_le_bytes()) |
| } |
| |
| /// Returns the u64 representation of the header value. |
| pub fn as_uint64(&self) -> Result<u64, IggyError> { |
| if self.kind != HeaderKind::Uint64 { |
| return Err(IggyError::InvalidHeaderValue); |
| } |
| |
| let value = self.value.to_vec().try_into(); |
| if value.is_err() { |
| return Err(IggyError::InvalidHeaderValue); |
| } |
| |
| Ok(u64::from_le_bytes(value.unwrap())) |
| } |
| |
| /// Creates a new header value from the specified u128. |
| pub fn from_uint128(value: u128) -> Result<Self, IggyError> { |
| Self::from(HeaderKind::Uint128, &value.to_le_bytes()) |
| } |
| |
| /// Returns the u128 representation of the header value. |
| pub fn as_uint128(&self) -> Result<u128, IggyError> { |
| if self.kind != HeaderKind::Uint128 { |
| return Err(IggyError::InvalidHeaderValue); |
| } |
| |
| let value = self.value.to_vec().try_into(); |
| if value.is_err() { |
| return Err(IggyError::InvalidHeaderValue); |
| } |
| |
| Ok(u128::from_le_bytes(value.unwrap())) |
| } |
| |
| /// Creates a new header value from the specified f32. |
| pub fn from_float32(value: f32) -> Result<Self, IggyError> { |
| Self::from(HeaderKind::Float32, &value.to_le_bytes()) |
| } |
| |
| /// Returns the f32 representation of the header value. |
| pub fn as_float32(&self) -> Result<f32, IggyError> { |
| if self.kind != HeaderKind::Float32 { |
| return Err(IggyError::InvalidHeaderValue); |
| } |
| |
| let value = self.value.to_vec().try_into(); |
| if value.is_err() { |
| return Err(IggyError::InvalidHeaderValue); |
| } |
| |
| Ok(f32::from_le_bytes(value.unwrap())) |
| } |
| |
| /// Creates a new header value from the specified f64. |
| pub fn from_float64(value: f64) -> Result<Self, IggyError> { |
| Self::from(HeaderKind::Float64, &value.to_le_bytes()) |
| } |
| |
| /// Returns the f64 representation of the header value. |
| pub fn as_float64(&self) -> Result<f64, IggyError> { |
| if self.kind != HeaderKind::Float64 { |
| return Err(IggyError::InvalidHeaderValue); |
| } |
| |
| let value = self.value.to_vec().try_into(); |
| if value.is_err() { |
| return Err(IggyError::InvalidHeaderValue); |
| } |
| |
| Ok(f64::from_le_bytes(value.unwrap())) |
| } |
| |
| /// Creates a new header value from the specified kind and value. |
| fn from(kind: HeaderKind, value: &[u8]) -> Result<Self, IggyError> { |
| if value.is_empty() || value.len() > 255 { |
| return Err(IggyError::InvalidHeaderValue); |
| } |
| |
| Ok(Self { |
| kind, |
| value: Bytes::from(value.to_vec()), |
| }) |
| } |
| |
| /// Returns the string representation of the header value without the kind. |
| pub fn value_only_to_string(&self) -> String { |
| match self.kind { |
| HeaderKind::Raw => format!("{:?}", self.value), |
| HeaderKind::String => format!("{}", String::from_utf8_lossy(&self.value)), |
| HeaderKind::Bool => format!("{}", self.value[0] != 0), |
| HeaderKind::Int8 => format!( |
| "{}", |
| i8::from_le_bytes(self.value.to_vec().try_into().unwrap()) |
| ), |
| HeaderKind::Int16 => format!( |
| "{}", |
| i16::from_le_bytes(self.value.to_vec().try_into().unwrap()) |
| ), |
| HeaderKind::Int32 => format!( |
| "{}", |
| i32::from_le_bytes(self.value.to_vec().try_into().unwrap()) |
| ), |
| HeaderKind::Int64 => format!( |
| "{}", |
| i64::from_le_bytes(self.value.to_vec().try_into().unwrap()) |
| ), |
| HeaderKind::Int128 => format!( |
| "{}", |
| i128::from_le_bytes(self.value.to_vec().try_into().unwrap()) |
| ), |
| HeaderKind::Uint8 => format!( |
| "{}", |
| u8::from_le_bytes(self.value.to_vec().try_into().unwrap()) |
| ), |
| HeaderKind::Uint16 => format!( |
| "{}", |
| u16::from_le_bytes(self.value.to_vec().try_into().unwrap()) |
| ), |
| HeaderKind::Uint32 => format!( |
| "{}", |
| u32::from_le_bytes(self.value.to_vec().try_into().unwrap()) |
| ), |
| HeaderKind::Uint64 => format!( |
| "{}", |
| u64::from_le_bytes(self.value.to_vec().try_into().unwrap()) |
| ), |
| HeaderKind::Uint128 => format!( |
| "{}", |
| u128::from_le_bytes(self.value.to_vec().try_into().unwrap()) |
| ), |
| HeaderKind::Float32 => format!( |
| "{}", |
| f32::from_le_bytes(self.value.to_vec().try_into().unwrap()) |
| ), |
| HeaderKind::Float64 => format!( |
| "{}", |
| f64::from_le_bytes(self.value.to_vec().try_into().unwrap()) |
| ), |
| } |
| } |
| } |
| |
| impl BytesSerializable for HashMap<HeaderKey, HeaderValue> { |
| fn as_bytes(&self) -> Bytes { |
| if self.is_empty() { |
| return Bytes::new(); |
| } |
| |
| let mut bytes = BytesMut::new(); |
| for (key, value) in self { |
| #[allow(clippy::cast_possible_truncation)] |
| bytes.put_u32_le(key.0.len() as u32); |
| bytes.put_slice(key.0.as_bytes()); |
| bytes.put_u8(value.kind.as_code()); |
| #[allow(clippy::cast_possible_truncation)] |
| bytes.put_u32_le(value.value.len() as u32); |
| bytes.put_slice(&value.value); |
| } |
| |
| bytes.freeze() |
| } |
| |
| fn from_bytes(bytes: Bytes) -> Result<Self, IggyError> |
| where |
| Self: Sized, |
| { |
| if bytes.is_empty() { |
| return Ok(Self::new()); |
| } |
| |
| let mut headers = Self::new(); |
| let mut position = 0; |
| while position < bytes.len() { |
| let key_length = u32::from_le_bytes(bytes[position..position + 4].try_into()?) as usize; |
| if key_length == 0 || key_length > 255 { |
| return Err(IggyError::InvalidHeaderKey); |
| } |
| position += 4; |
| let key = String::from_utf8(bytes[position..position + key_length].to_vec()); |
| if key.is_err() { |
| return Err(IggyError::InvalidHeaderKey); |
| } |
| let key = key.unwrap(); |
| position += key_length; |
| let kind = HeaderKind::from_code(bytes[position])?; |
| position += 1; |
| let value_length = |
| u32::from_le_bytes(bytes[position..position + 4].try_into()?) as usize; |
| if value_length == 0 || value_length > 255 { |
| return Err(IggyError::InvalidHeaderValue); |
| } |
| position += 4; |
| let value = bytes[position..position + value_length].to_vec(); |
| position += value_length; |
| headers.insert( |
| HeaderKey(key), |
| HeaderValue { |
| kind, |
| value: Bytes::from(value), |
| }, |
| ); |
| } |
| |
| Ok(headers) |
| } |
| } |
| |
| /// Returns the size in bytes of the specified headers. |
| pub fn get_headers_size_bytes(headers: &Option<HashMap<HeaderKey, HeaderValue>>) -> u32 { |
| // Headers length field |
| let mut size = 4; |
| if let Some(headers) = headers { |
| for (key, value) in headers { |
| // Key length + Key + Kind + Value length + Value |
| size += 4 + key.as_str().len() as u32 + 1 + 4 + value.value.len() as u32; |
| } |
| } |
| size |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| |
| #[test] |
| fn header_key_should_be_created_for_valid_value() { |
| let value = "key-1"; |
| let header_key = HeaderKey::new(value); |
| assert!(header_key.is_ok()); |
| assert_eq!(header_key.unwrap().0, value); |
| } |
| |
| #[test] |
| fn header_key_should_not_be_created_for_empty_value() { |
| let value = ""; |
| let header_key = HeaderKey::new(value); |
| assert!(header_key.is_err()); |
| let error = header_key.unwrap_err(); |
| assert_eq!(error.as_code(), IggyError::InvalidHeaderKey.as_code()); |
| } |
| |
| #[test] |
| fn header_key_should_not_be_created_for_too_long_value() { |
| let value = "a".repeat(256); |
| let header_key = HeaderKey::new(&value); |
| assert!(header_key.is_err()); |
| let error = header_key.unwrap_err(); |
| assert_eq!(error.as_code(), IggyError::InvalidHeaderKey.as_code()); |
| } |
| |
| #[test] |
| fn header_value_should_not_be_created_for_empty_value() { |
| let header_value = HeaderValue::from(HeaderKind::Raw, &[]); |
| assert!(header_value.is_err()); |
| let error = header_value.unwrap_err(); |
| assert_eq!(error.as_code(), IggyError::InvalidHeaderValue.as_code()); |
| } |
| |
| #[test] |
| fn header_value_should_not_be_created_for_too_long_value() { |
| let value = b"a".repeat(256); |
| let header_value = HeaderValue::from(HeaderKind::Raw, &value); |
| assert!(header_value.is_err()); |
| let error = header_value.unwrap_err(); |
| assert_eq!(error.as_code(), IggyError::InvalidHeaderValue.as_code()); |
| } |
| |
| #[test] |
| fn header_value_should_be_created_from_raw_bytes() { |
| let value = b"Value 1"; |
| let header_value = HeaderValue::from_raw(value); |
| assert!(header_value.is_ok()); |
| assert_eq!(header_value.unwrap().value.as_ref(), value); |
| } |
| |
| #[test] |
| fn header_value_should_be_created_from_str() { |
| let value = "Value 1"; |
| let header_value = HeaderValue::from_str(value); |
| assert!(header_value.is_ok()); |
| let header_value = header_value.unwrap(); |
| assert_eq!(header_value.kind, HeaderKind::String); |
| assert_eq!(header_value.value, value.as_bytes()); |
| assert_eq!(header_value.as_str().unwrap(), value); |
| } |
| |
| #[test] |
| fn header_value_should_be_created_from_bool() { |
| let value = true; |
| let header_value = HeaderValue::from_bool(value); |
| assert!(header_value.is_ok()); |
| let header_value = header_value.unwrap(); |
| assert_eq!(header_value.kind, HeaderKind::Bool); |
| assert_eq!(header_value.value.as_ref(), if value { [1] } else { [0] }); |
| assert_eq!(header_value.as_bool().unwrap(), value); |
| } |
| |
| #[test] |
| fn header_value_should_be_created_from_int8() { |
| let value = 123; |
| let header_value = HeaderValue::from_int8(value); |
| assert!(header_value.is_ok()); |
| let header_value = header_value.unwrap(); |
| assert_eq!(header_value.kind, HeaderKind::Int8); |
| assert_eq!(header_value.value.as_ref(), value.to_le_bytes()); |
| assert_eq!(header_value.as_int8().unwrap(), value); |
| } |
| |
| #[test] |
| fn header_value_should_be_created_from_int16() { |
| let value = 12345; |
| let header_value = HeaderValue::from_int16(value); |
| assert!(header_value.is_ok()); |
| let header_value = header_value.unwrap(); |
| assert_eq!(header_value.kind, HeaderKind::Int16); |
| assert_eq!(header_value.value.as_ref(), value.to_le_bytes()); |
| assert_eq!(header_value.as_int16().unwrap(), value); |
| } |
| |
| #[test] |
| fn header_value_should_be_created_from_int32() { |
| let value = 123_456; |
| let header_value = HeaderValue::from_int32(value); |
| assert!(header_value.is_ok()); |
| let header_value = header_value.unwrap(); |
| assert_eq!(header_value.kind, HeaderKind::Int32); |
| assert_eq!(header_value.value.as_ref(), value.to_le_bytes()); |
| assert_eq!(header_value.as_int32().unwrap(), value); |
| } |
| |
| #[test] |
| fn header_value_should_be_created_from_int64() { |
| let value = 123_4567; |
| let header_value = HeaderValue::from_int64(value); |
| assert!(header_value.is_ok()); |
| let header_value = header_value.unwrap(); |
| assert_eq!(header_value.kind, HeaderKind::Int64); |
| assert_eq!(header_value.value.as_ref(), value.to_le_bytes()); |
| assert_eq!(header_value.as_int64().unwrap(), value); |
| } |
| |
| #[test] |
| fn header_value_should_be_created_from_int128() { |
| let value = 1234_5678; |
| let header_value = HeaderValue::from_int128(value); |
| assert!(header_value.is_ok()); |
| let header_value = header_value.unwrap(); |
| assert_eq!(header_value.kind, HeaderKind::Int128); |
| assert_eq!(header_value.value.as_ref(), value.to_le_bytes()); |
| assert_eq!(header_value.as_int128().unwrap(), value); |
| } |
| |
| #[test] |
| fn header_value_should_be_created_from_uint8() { |
| let value = 123; |
| let header_value = HeaderValue::from_uint8(value); |
| assert!(header_value.is_ok()); |
| let header_value = header_value.unwrap(); |
| assert_eq!(header_value.kind, HeaderKind::Uint8); |
| assert_eq!(header_value.value.as_ref(), value.to_le_bytes()); |
| assert_eq!(header_value.as_uint8().unwrap(), value); |
| } |
| |
| #[test] |
| fn header_value_should_be_created_from_uint16() { |
| let value = 12345; |
| let header_value = HeaderValue::from_uint16(value); |
| assert!(header_value.is_ok()); |
| let header_value = header_value.unwrap(); |
| assert_eq!(header_value.kind, HeaderKind::Uint16); |
| assert_eq!(header_value.value.as_ref(), value.to_le_bytes()); |
| assert_eq!(header_value.as_uint16().unwrap(), value); |
| } |
| |
| #[test] |
| fn header_value_should_be_created_from_uint32() { |
| let value = 123_456; |
| let header_value = HeaderValue::from_uint32(value); |
| assert!(header_value.is_ok()); |
| let header_value = header_value.unwrap(); |
| assert_eq!(header_value.kind, HeaderKind::Uint32); |
| assert_eq!(header_value.value.as_ref(), value.to_le_bytes()); |
| assert_eq!(header_value.as_uint32().unwrap(), value); |
| } |
| |
| #[test] |
| fn header_value_should_be_created_from_uint64() { |
| let value = 123_4567; |
| let header_value = HeaderValue::from_uint64(value); |
| assert!(header_value.is_ok()); |
| let header_value = header_value.unwrap(); |
| assert_eq!(header_value.kind, HeaderKind::Uint64); |
| assert_eq!(header_value.value.as_ref(), value.to_le_bytes()); |
| assert_eq!(header_value.as_uint64().unwrap(), value); |
| } |
| |
| #[test] |
| fn header_value_should_be_created_from_uint128() { |
| let value = 1234_5678; |
| let header_value = HeaderValue::from_uint128(value); |
| assert!(header_value.is_ok()); |
| let header_value = header_value.unwrap(); |
| assert_eq!(header_value.kind, HeaderKind::Uint128); |
| assert_eq!(header_value.value.as_ref(), value.to_le_bytes()); |
| assert_eq!(header_value.as_uint128().unwrap(), value); |
| } |
| |
| #[test] |
| fn header_value_should_be_created_from_float32() { |
| let value = 123.01; |
| let header_value = HeaderValue::from_float32(value); |
| assert!(header_value.is_ok()); |
| let header_value = header_value.unwrap(); |
| assert_eq!(header_value.kind, HeaderKind::Float32); |
| assert_eq!(header_value.value.as_ref(), value.to_le_bytes()); |
| assert_eq!(header_value.as_float32().unwrap(), value); |
| } |
| |
| #[test] |
| fn header_value_should_be_created_from_float64() { |
| let value = 1234.01234; |
| let header_value = HeaderValue::from_float64(value); |
| assert!(header_value.is_ok()); |
| let header_value = header_value.unwrap(); |
| assert_eq!(header_value.kind, HeaderKind::Float64); |
| assert_eq!(header_value.value.as_ref(), value.to_le_bytes()); |
| assert_eq!(header_value.as_float64().unwrap(), value); |
| } |
| |
| #[test] |
| fn header_value_should_be_created_string_from_kind_and_value_str() { |
| let value = "Value 1"; |
| let header_value = HeaderValue::from_kind_str_and_value_str("string", value); |
| assert!(header_value.is_ok()); |
| let header_value = header_value.unwrap(); |
| assert_eq!(header_value.kind, HeaderKind::String); |
| assert_eq!(header_value.value, value.as_bytes()); |
| assert_eq!(header_value.as_str().unwrap(), value); |
| } |
| |
| #[test] |
| fn header_value_should_be_created_float_from_kind_and_value_str() { |
| let value: f64 = 1234.01234; |
| let header_value = HeaderValue::from_kind_str_and_value_str("float64", &value.to_string()); |
| assert!(header_value.is_ok()); |
| let header_value = header_value.unwrap(); |
| assert_eq!(header_value.kind, HeaderKind::Float64); |
| assert_eq!(header_value.value.as_ref(), value.to_le_bytes()); |
| assert_eq!(header_value.as_float64().unwrap(), value); |
| } |
| |
| #[test] |
| fn header_value_should_be_created_raw_from_kind_and_value_str() { |
| let value = "Value 1"; |
| let header_value = HeaderValue::from_kind_str_and_value_str("raw", value); |
| assert!(header_value.is_ok()); |
| let header_value = header_value.unwrap(); |
| assert_eq!(header_value.kind, HeaderKind::Raw); |
| assert_eq!(header_value.value, value.as_bytes()); |
| } |
| |
| #[test] |
| fn header_value_should_be_created_bool_from_kind_and_value_str() { |
| let value = "true"; |
| let header_value = HeaderValue::from_kind_str_and_value_str("bool", value); |
| assert!(header_value.is_ok()); |
| let header_value = header_value.unwrap(); |
| assert_eq!(header_value.kind, HeaderKind::Bool); |
| assert_eq!(header_value.value, vec![1]); |
| assert!(header_value.as_bool().unwrap()); |
| } |
| |
| #[test] |
| fn header_value_should_be_created_int8_from_kind_and_value_str() { |
| let value = "123"; |
| let header_value = HeaderValue::from_kind_str_and_value_str("int8", value); |
| assert!(header_value.is_ok()); |
| let header_value = header_value.unwrap(); |
| assert_eq!(header_value.kind, HeaderKind::Int8); |
| assert_eq!(header_value.value, vec![123]); |
| assert_eq!(header_value.as_int8().unwrap(), 123); |
| } |
| |
| #[test] |
| fn header_value_should_be_created_int16_from_kind_and_value_str() { |
| let value = "1234"; |
| let header_value = HeaderValue::from_kind_str_and_value_str("int16", value); |
| assert!(header_value.is_ok()); |
| let header_value = header_value.unwrap(); |
| assert_eq!(header_value.kind, HeaderKind::Int16); |
| assert_eq!( |
| header_value.value.as_ref(), |
| value.parse::<i16>().unwrap().to_le_bytes() |
| ); |
| assert_eq!( |
| header_value.as_int16().unwrap(), |
| value.parse::<i16>().unwrap() |
| ); |
| } |
| |
| #[test] |
| fn header_value_should_be_created_int32_from_kind_and_value_str() { |
| let value = "123456"; |
| let header_value = HeaderValue::from_kind_str_and_value_str("int32", value); |
| assert!(header_value.is_ok()); |
| let header_value = header_value.unwrap(); |
| assert_eq!(header_value.kind, HeaderKind::Int32); |
| assert_eq!( |
| header_value.value.as_ref(), |
| value.parse::<i32>().unwrap().to_le_bytes() |
| ); |
| assert_eq!( |
| header_value.as_int32().unwrap(), |
| value.parse::<i32>().unwrap() |
| ); |
| } |
| |
| #[test] |
| fn header_value_should_be_created_int64_from_kind_and_value_str() { |
| let value = "123456789"; |
| let header_value = HeaderValue::from_kind_str_and_value_str("int64", value); |
| assert!(header_value.is_ok()); |
| let header_value = header_value.unwrap(); |
| assert_eq!(header_value.kind, HeaderKind::Int64); |
| assert_eq!( |
| header_value.value.as_ref(), |
| value.parse::<i64>().unwrap().to_le_bytes() |
| ); |
| assert_eq!( |
| header_value.as_int64().unwrap(), |
| value.parse::<i64>().unwrap() |
| ); |
| } |
| |
| #[test] |
| fn header_value_should_be_created_int128_from_kind_and_value_str() { |
| let value = "123456789123456789"; |
| let header_value = HeaderValue::from_kind_str_and_value_str("int128", value); |
| assert!(header_value.is_ok()); |
| let header_value = header_value.unwrap(); |
| assert_eq!(header_value.kind, HeaderKind::Int128); |
| assert_eq!( |
| header_value.value.as_ref(), |
| value.parse::<i128>().unwrap().to_le_bytes() |
| ); |
| assert_eq!( |
| header_value.as_int128().unwrap(), |
| value.parse::<i128>().unwrap() |
| ); |
| } |
| |
| #[test] |
| fn header_value_should_be_created_uint8_from_kind_and_value_str() { |
| let value = "123"; |
| let header_value = HeaderValue::from_kind_str_and_value_str("uint8", value); |
| assert!(header_value.is_ok()); |
| let header_value = header_value.unwrap(); |
| assert_eq!(header_value.kind, HeaderKind::Uint8); |
| assert_eq!(header_value.value, vec![123]); |
| assert_eq!(header_value.as_uint8().unwrap(), 123); |
| } |
| |
| #[test] |
| fn header_value_should_be_created_uint16_from_kind_and_value_str() { |
| let value = "12345"; |
| let header_value = HeaderValue::from_kind_str_and_value_str("uint16", value); |
| assert!(header_value.is_ok()); |
| let header_value = header_value.unwrap(); |
| assert_eq!(header_value.kind, HeaderKind::Uint16); |
| assert_eq!( |
| header_value.value.as_ref(), |
| value.parse::<u16>().unwrap().to_le_bytes() |
| ); |
| assert_eq!( |
| header_value.as_uint16().unwrap(), |
| value.parse::<u16>().unwrap() |
| ); |
| } |
| |
| #[test] |
| fn header_value_should_be_created_uint32_from_kind_and_value_str() { |
| let value = "123456"; |
| let header_value = HeaderValue::from_kind_str_and_value_str("uint32", value); |
| assert!(header_value.is_ok()); |
| let header_value = header_value.unwrap(); |
| assert_eq!(header_value.kind, HeaderKind::Uint32); |
| assert_eq!( |
| header_value.value.as_ref(), |
| value.parse::<u32>().unwrap().to_le_bytes() |
| ); |
| assert_eq!( |
| header_value.as_uint32().unwrap(), |
| value.parse::<u32>().unwrap() |
| ); |
| } |
| |
| #[test] |
| fn header_value_should_be_created_uint64_from_kind_and_value_str() { |
| let value = "123456789"; |
| let header_value = HeaderValue::from_kind_str_and_value_str("uint64", value); |
| assert!(header_value.is_ok()); |
| let header_value = header_value.unwrap(); |
| assert_eq!(header_value.kind, HeaderKind::Uint64); |
| assert_eq!( |
| header_value.value.as_ref(), |
| value.parse::<u64>().unwrap().to_le_bytes() |
| ); |
| assert_eq!( |
| header_value.as_uint64().unwrap(), |
| value.parse::<u64>().unwrap() |
| ); |
| } |
| |
| #[test] |
| fn header_value_should_be_created_uint128_from_kind_and_value_str() { |
| let value = "123456789123456789"; |
| let header_value = HeaderValue::from_kind_str_and_value_str("uint128", value); |
| assert!(header_value.is_ok()); |
| let header_value = header_value.unwrap(); |
| assert_eq!(header_value.kind, HeaderKind::Uint128); |
| assert_eq!( |
| header_value.value.as_ref(), |
| value.parse::<u128>().unwrap().to_le_bytes() |
| ); |
| assert_eq!( |
| header_value.as_uint128().unwrap(), |
| value.parse::<u128>().unwrap() |
| ); |
| } |
| |
| #[test] |
| fn header_value_should_be_created_float32_from_kind_and_value_str() { |
| let value = "123.01"; |
| let header_value = HeaderValue::from_kind_str_and_value_str("float32", value); |
| assert!(header_value.is_ok()); |
| let header_value = header_value.unwrap(); |
| assert_eq!(header_value.kind, HeaderKind::Float32); |
| assert_eq!( |
| header_value.value.as_ref(), |
| value.parse::<f32>().unwrap().to_le_bytes() |
| ); |
| assert_eq!( |
| header_value.as_float32().unwrap(), |
| value.parse::<f32>().unwrap() |
| ); |
| } |
| |
| #[test] |
| fn header_value_should_be_created_float64_from_kind_and_value_str() { |
| let value = "1234.01234"; |
| let header_value = HeaderValue::from_kind_str_and_value_str("float64", value); |
| assert!(header_value.is_ok()); |
| let header_value = header_value.unwrap(); |
| assert_eq!(header_value.kind, HeaderKind::Float64); |
| assert_eq!( |
| header_value.value.as_ref(), |
| value.parse::<f64>().unwrap().to_le_bytes() |
| ); |
| assert_eq!( |
| header_value.as_float64().unwrap(), |
| value.parse::<f64>().unwrap() |
| ); |
| } |
| |
| #[test] |
| fn value_only_to_string_for_string_kind() { |
| let header_value = HeaderValue::from_str("Hello").unwrap(); |
| assert_eq!(header_value.value_only_to_string(), "Hello"); |
| } |
| |
| #[test] |
| fn value_only_to_string_for_bool_kind() { |
| let header_value = HeaderValue::from_bool(true).unwrap(); |
| assert_eq!(header_value.value_only_to_string(), "true"); |
| } |
| |
| #[test] |
| fn value_only_to_string_for_int8_kind() { |
| let header_value = HeaderValue::from_int8(123).unwrap(); |
| assert_eq!(header_value.value_only_to_string(), "123"); |
| } |
| |
| #[test] |
| fn value_only_to_string_for_int16_kind() { |
| let header_value = HeaderValue::from_int16(12345).unwrap(); |
| assert_eq!(header_value.value_only_to_string(), "12345"); |
| } |
| |
| #[test] |
| fn value_only_to_string_for_int32_kind() { |
| let header_value = HeaderValue::from_int32(123456).unwrap(); |
| assert_eq!(header_value.value_only_to_string(), "123456"); |
| } |
| |
| #[test] |
| fn value_only_to_string_for_int64_kind() { |
| let header_value = HeaderValue::from_int64(123456789).unwrap(); |
| assert_eq!(header_value.value_only_to_string(), "123456789"); |
| } |
| |
| #[test] |
| fn value_only_to_string_for_int128_kind() { |
| let header_value = HeaderValue::from_int128(123456789123456789).unwrap(); |
| assert_eq!(header_value.value_only_to_string(), "123456789123456789"); |
| } |
| |
| #[test] |
| fn value_only_to_string_for_uint8_kind() { |
| let header_value = HeaderValue::from_uint8(123).unwrap(); |
| assert_eq!(header_value.value_only_to_string(), "123"); |
| } |
| |
| #[test] |
| fn value_only_to_string_for_uint16_kind() { |
| let header_value = HeaderValue::from_uint16(12345).unwrap(); |
| assert_eq!(header_value.value_only_to_string(), "12345"); |
| } |
| |
| #[test] |
| fn value_only_to_string_for_uint32_kind() { |
| let header_value = HeaderValue::from_uint32(123456).unwrap(); |
| assert_eq!(header_value.value_only_to_string(), "123456"); |
| } |
| |
| #[test] |
| fn value_only_to_string_for_uint64_kind() { |
| let header_value = HeaderValue::from_uint64(123456789).unwrap(); |
| assert_eq!(header_value.value_only_to_string(), "123456789"); |
| } |
| |
| #[test] |
| fn value_only_to_string_for_uint128_kind() { |
| let header_value = HeaderValue::from_uint128(123456789123456789).unwrap(); |
| assert_eq!(header_value.value_only_to_string(), "123456789123456789"); |
| } |
| |
| #[test] |
| fn value_only_to_string_for_float32_kind() { |
| let header_value = HeaderValue::from_float32(123.01).unwrap(); |
| assert_eq!(header_value.value_only_to_string(), "123.01"); |
| } |
| |
| #[test] |
| fn value_only_to_string_for_float64_kind() { |
| let header_value = HeaderValue::from_float64(1234.01234).unwrap(); |
| assert_eq!(header_value.value_only_to_string(), "1234.01234"); |
| } |
| |
| #[test] |
| fn should_be_serialized_as_bytes() { |
| let mut headers = HashMap::new(); |
| headers.insert( |
| HeaderKey::new("key-1").unwrap(), |
| HeaderValue::from_str("Value 1").unwrap(), |
| ); |
| headers.insert( |
| HeaderKey::new("key 1").unwrap(), |
| HeaderValue::from_uint64(12345).unwrap(), |
| ); |
| headers.insert( |
| HeaderKey::new("key_3").unwrap(), |
| HeaderValue::from_bool(true).unwrap(), |
| ); |
| |
| let bytes = headers.as_bytes(); |
| |
| let mut position = 0; |
| let mut headers_count = 0; |
| while position < bytes.len() { |
| let key_length = |
| u32::from_le_bytes(bytes[position..position + 4].try_into().unwrap()) as usize; |
| position += 4; |
| let key = String::from_utf8(bytes[position..position + key_length].to_vec()).unwrap(); |
| position += key_length; |
| let kind = HeaderKind::from_code(bytes[position]).unwrap(); |
| position += 1; |
| let value_length = |
| u32::from_le_bytes(bytes[position..position + 4].try_into().unwrap()) as usize; |
| position += 4; |
| let value = bytes[position..position + value_length].to_vec(); |
| position += value_length; |
| let header = headers.get(&HeaderKey::new(&key).unwrap()); |
| assert!(header.is_some()); |
| let header = header.unwrap(); |
| assert_eq!(header.kind, kind); |
| assert_eq!(header.value, value); |
| headers_count += 1; |
| } |
| |
| assert_eq!(headers_count, headers.len()); |
| } |
| |
| #[test] |
| fn should_be_deserialized_from_bytes() { |
| let mut headers = HashMap::new(); |
| headers.insert( |
| HeaderKey::new("key-1").unwrap(), |
| HeaderValue::from_str("Value 1").unwrap(), |
| ); |
| headers.insert( |
| HeaderKey::new("key 2").unwrap(), |
| HeaderValue::from_uint64(12345).unwrap(), |
| ); |
| headers.insert( |
| HeaderKey::new("key_3").unwrap(), |
| HeaderValue::from_bool(true).unwrap(), |
| ); |
| |
| let mut bytes = BytesMut::new(); |
| for (key, value) in &headers { |
| bytes.put_u32_le(key.0.len() as u32); |
| bytes.put_slice(key.0.as_bytes()); |
| bytes.put_u8(value.kind.as_code()); |
| bytes.put_u32_le(value.value.len() as u32); |
| bytes.put_slice(&value.value); |
| } |
| |
| let deserialized_headers = HashMap::<HeaderKey, HeaderValue>::from_bytes(bytes.freeze()); |
| |
| assert!(deserialized_headers.is_ok()); |
| let deserialized_headers = deserialized_headers.unwrap(); |
| assert_eq!(deserialized_headers.len(), headers.len()); |
| |
| for (key, value) in &headers { |
| let deserialized_value = deserialized_headers.get(key); |
| assert!(deserialized_value.is_some()); |
| let deserialized_value = deserialized_value.unwrap(); |
| assert_eq!(deserialized_value.kind, value.kind); |
| assert_eq!(deserialized_value.value, value.value); |
| } |
| } |
| } |