use alloc::string::String;
use alloc::vec::Vec;
use core::fmt;
use crate::validate::{is_valid_token, trim_ows};
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum ContentEncodingError {
Empty,
InvalidFormat,
InvalidEncoding,
}
impl fmt::Display for ContentEncodingError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ContentEncodingError::Empty => write!(f, "empty Content-Encoding"),
ContentEncodingError::InvalidFormat => {
write!(f, "invalid Content-Encoding format")
}
ContentEncodingError::InvalidEncoding => {
write!(f, "invalid Content-Encoding token")
}
}
}
}
impl core::error::Error for ContentEncodingError {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ContentCoding {
Gzip,
Deflate,
Compress,
Identity,
Other(String),
}
impl ContentCoding {
pub fn as_str(&self) -> &str {
match self {
ContentCoding::Gzip => "gzip",
ContentCoding::Deflate => "deflate",
ContentCoding::Compress => "compress",
ContentCoding::Identity => "identity",
ContentCoding::Other(value) => value.as_str(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ContentEncoding {
encodings: Vec<ContentCoding>,
}
impl ContentEncoding {
pub fn parse(input: &str) -> Result<Self, ContentEncodingError> {
let input = trim_ows(input);
let mut encodings = Vec::new();
if !input.is_empty() {
for part in input.split(',') {
let part = trim_ows(part);
if part.is_empty() {
continue;
}
let coding = parse_coding(part)?;
encodings.push(coding);
}
}
Ok(ContentEncoding { encodings })
}
pub fn encodings(&self) -> &[ContentCoding] {
&self.encodings
}
pub fn has_gzip(&self) -> bool {
self.encodings
.iter()
.any(|coding| matches!(coding, ContentCoding::Gzip))
}
pub fn has_deflate(&self) -> bool {
self.encodings
.iter()
.any(|coding| matches!(coding, ContentCoding::Deflate))
}
pub fn has_compress(&self) -> bool {
self.encodings
.iter()
.any(|coding| matches!(coding, ContentCoding::Compress))
}
pub fn has_identity(&self) -> bool {
self.encodings
.iter()
.any(|coding| matches!(coding, ContentCoding::Identity))
}
}
impl fmt::Display for ContentEncoding {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let values: Vec<&str> = self.encodings.iter().map(ContentCoding::as_str).collect();
write!(f, "{}", values.join(", "))
}
}
fn parse_coding(token: &str) -> Result<ContentCoding, ContentEncodingError> {
if token.is_empty() {
return Err(ContentEncodingError::InvalidFormat);
}
if !is_valid_token(token) {
return Err(ContentEncodingError::InvalidEncoding);
}
let normalized = token.to_ascii_lowercase();
let coding = match normalized.as_str() {
"gzip" => ContentCoding::Gzip,
"deflate" => ContentCoding::Deflate,
"compress" => ContentCoding::Compress,
"identity" => ContentCoding::Identity,
_ => ContentCoding::Other(normalized),
};
Ok(coding)
}