use std::{
fmt::{self, Display},
ops::{Deref, DerefMut},
};
use crate::error::{ParseError, Result};
#[derive(Debug, Hash, Clone, PartialEq, Eq)]
pub struct Header {
pub name: HeaderType,
pub value: String,
}
pub struct HeaderParams<'a> {
pub value: &'a str,
params: Vec<[&'a str; 2]>,
}
#[derive(Debug, Hash, Clone, PartialEq, Eq, Default)]
pub struct Headers(pub(crate) Vec<Header>);
impl Header {
pub fn new(name: impl Into<HeaderType>, value: impl AsRef<str>) -> Header {
Header {
name: name.into(),
value: value.as_ref().to_owned(),
}
}
pub fn from_string(header: impl AsRef<str>) -> Result<Header> {
let header = header.as_ref();
let mut split_header = header.splitn(2, ':');
if split_header.clone().count() != 2 {
return Err(ParseError::InvalidHeader.into());
}
let name = split_header
.next()
.ok_or(ParseError::InvalidHeader)?
.trim()
.into();
let value = split_header
.next()
.ok_or(ParseError::InvalidHeader)?
.trim()
.into();
Ok(Header { name, value })
}
pub fn params(&self) -> HeaderParams {
HeaderParams::new(self.value.as_str())
}
}
impl<'a> HeaderParams<'a> {
fn new(value: &'a str) -> Self {
let mut params = Vec::new();
let mut parts = value.split(';');
let value = parts.next().unwrap_or_default();
for i in parts {
let split = i.splitn(2, '=').collect::<Vec<_>>();
if split.len() != 2 {
break;
}
let key = match split.first() {
Some(key) => key.trim(),
None => break,
};
let value = match split.get(1) {
Some(value) => value.trim(),
None => break,
};
params.push([key, value]);
}
Self { value, params }
}
pub fn has(&self, name: impl AsRef<str>) -> bool {
let name = name.as_ref();
self.params.iter().any(|[key, _]| key == &name)
}
pub fn get(&self, name: impl AsRef<str>) -> Option<&str> {
let name = name.as_ref();
self.params
.iter()
.find(|[key, _]| key == &name)
.map(|[_, value]| *value)
}
}
impl Deref for Headers {
type Target = Vec<Header>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Headers {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<'a> Deref for HeaderParams<'a> {
type Target = Vec<[&'a str; 2]>;
fn deref(&self) -> &Self::Target {
self.params.as_ref()
}
}
impl<'a> DerefMut for HeaderParams<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.params.as_mut()
}
}
impl Headers {
pub fn has(&self, name: impl Into<HeaderType>) -> bool {
let name = name.into();
self.iter().any(|x| x.name == name)
}
pub fn add(&mut self, name: impl Into<HeaderType>, value: impl AsRef<str>) {
self.0.push(Header::new(name, value));
}
pub fn get(&self, name: impl Into<HeaderType>) -> Option<&str> {
let name = name.into();
self.iter()
.find(|x| x.name == name)
.map(|x| x.value.as_str())
}
pub fn get_mut(&mut self, name: impl Into<HeaderType>) -> Option<&mut String> {
let name = name.into();
self.iter_mut()
.find(|x| x.name == name)
.map(|x| &mut x.value)
}
pub fn add_header(&mut self, header: Header) {
self.0.push(header);
}
pub fn get_header(&self, name: impl Into<HeaderType>) -> Option<&Header> {
let name = name.into();
self.iter().find(|x| x.name == name)
}
pub fn get_header_mut(&mut self, name: impl Into<HeaderType>) -> Option<&mut Header> {
let name = name.into();
self.iter_mut().find(|x| x.name == name)
}
}
impl fmt::Display for Header {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}: {}", self.name, self.value)
}
}
pub(crate) fn headers_to_string(headers: &[Header]) -> String {
let out = headers
.iter()
.map(Header::to_string)
.fold(String::new(), |acc, i| acc + &i + "\r\n");
out[..out.len() - 2].to_owned()
}
#[derive(Debug, Hash, Clone, PartialEq, Eq)]
pub enum HeaderType {
Accept,
AcceptCharset,
AcceptEncoding,
AcceptLanguage,
Connection,
ContentEncoding,
ContentLength,
ContentType,
Cookie,
Date,
Host,
Location,
Referer,
Server,
SetCookie,
TransferEncoding,
Upgrade,
UserAgent,
Via,
XForwardedFor,
Custom(String),
}
impl From<&HeaderType> for HeaderType {
fn from(s: &HeaderType) -> Self {
s.to_owned()
}
}
impl<T: AsRef<str>> From<T> for HeaderType {
fn from(s: T) -> Self {
HeaderType::from_str(s.as_ref())
}
}
impl HeaderType {
#[rustfmt::skip]
fn from_str(s: &str) -> Self {
match s.to_ascii_lowercase().as_str() {
"accept" => HeaderType::Accept,
"accept-charset" => HeaderType::AcceptCharset,
"accept-encoding" => HeaderType::AcceptEncoding,
"accept-language" => HeaderType::AcceptLanguage,
"connection" => HeaderType::Connection,
"content-encoding" => HeaderType::ContentEncoding,
"content-length" => HeaderType::ContentLength,
"content-type" => HeaderType::ContentType,
"cookie" => HeaderType::Cookie,
"date" => HeaderType::Date,
"host" => HeaderType::Host,
"location" => HeaderType::Location,
"referer" => HeaderType::Referer,
"server" => HeaderType::Server,
"set-cookie" => HeaderType::SetCookie,
"transfer-encoding" => HeaderType::TransferEncoding,
"upgrade" => HeaderType::Upgrade,
"user-agent" => HeaderType::UserAgent,
"via" => HeaderType::Via,
"x-forwarded-for" => HeaderType::XForwardedFor,
_ => HeaderType::Custom(s.to_string()),
}
}
}
impl Display for HeaderType {
#[rustfmt::skip]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
HeaderType::Accept => "Accept",
HeaderType::AcceptCharset => "Accept-Charset",
HeaderType::AcceptEncoding => "Accept-Encoding",
HeaderType::AcceptLanguage => "Accept-Language",
HeaderType::Connection => "Connection",
HeaderType::ContentEncoding => "Content-Encoding",
HeaderType::ContentLength => "Content-Length",
HeaderType::ContentType => "Content-Type",
HeaderType::Cookie => "Cookie",
HeaderType::Date => "Date",
HeaderType::Host => "Host",
HeaderType::Location => "Location",
HeaderType::Referer => "Referer",
HeaderType::Server => "Server",
HeaderType::SetCookie => "Set-Cookie",
HeaderType::TransferEncoding => "Transfer-Encoding",
HeaderType::Upgrade => "Upgrade",
HeaderType::UserAgent => "User-Agent",
HeaderType::Via => "Via",
HeaderType::XForwardedFor => "X-Forwarded-For",
HeaderType::Custom(s) => s,
}
)
}
}