use alloc::string::String;
use core::{
fmt::{self, Display},
ops::Deref,
};
use bytestring::ByteString;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct HeaderValue(ByteString);
impl HeaderValue {
#[must_use]
pub fn from_static(value: &'static str) -> Self {
Self::try_from(ByteString::from_static(value)).expect("invalid HeaderValue")
}
#[must_use]
#[expect(
clippy::missing_panics_doc,
reason = "The header validation is only made in debug"
)]
pub fn from_dangerous_value(value: ByteString) -> Self {
if cfg!(debug_assertions) {
if let Err(err) = validate_header_value(&value) {
panic!("HeaderValue {value:?} isn't valid {err:?}");
}
}
Self(value)
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
impl Display for HeaderValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&self.0, f)
}
}
impl TryFrom<ByteString> for HeaderValue {
type Error = HeaderValueValidateError;
fn try_from(value: ByteString) -> Result<Self, Self::Error> {
validate_header_value(&value)?;
Ok(Self::from_dangerous_value(value))
}
}
impl TryFrom<String> for HeaderValue {
type Error = HeaderValueValidateError;
fn try_from(value: String) -> Result<Self, Self::Error> {
validate_header_value(&value)?;
Ok(Self::from_dangerous_value(value.into()))
}
}
impl From<HeaderValue> for ByteString {
fn from(value: HeaderValue) -> Self {
value.0
}
}
impl AsRef<[u8]> for HeaderValue {
fn as_ref(&self) -> &[u8] {
self.as_str().as_bytes()
}
}
impl AsRef<str> for HeaderValue {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl Deref for HeaderValue {
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
#[derive(Debug, thiserror::Error)]
pub enum HeaderValueValidateError {
#[error("HeaderValue is empty")]
Empty,
#[error("HeaderValue is too long")]
TooLong,
#[error("HeaderValue contained an illegal whitespace character")]
IllegalCharacter,
}
fn validate_header_value(header_value: &str) -> Result<(), HeaderValueValidateError> {
if header_value.is_empty() {
return Err(HeaderValueValidateError::Empty);
}
if header_value.len() > 1024 {
return Err(HeaderValueValidateError::TooLong);
}
if header_value.chars().any(char::is_whitespace) {
return Err(HeaderValueValidateError::IllegalCharacter);
}
Ok(())
}