use ahash::AHashMap;
use std::fmt;
use std::hash::Hash;
use std::hash::Hasher;
use std::ops::Deref;
macro_rules! define_header_keys {
($($name:ident => $string:expr),* $(,)?) => {
#[derive(Debug, Clone)]
pub enum HeaderKey {
$($name,)*
Custom(String),
}
impl HeaderKey {
#[inline]
pub fn as_str(&self) -> &str {
match self {
$(
HeaderKey::$name => $string,
)*
HeaderKey::Custom(s) => s.as_str(),
}
}
#[inline]
pub fn from_str(s: &str) -> Option<Self> {
let s_trimmed = s.trim();
let s_lower = s_trimmed.to_ascii_lowercase();
match s_lower.as_str() {
$(
s if s == $string.to_ascii_lowercase() => Some(HeaderKey::$name),
)*
_ => Some(HeaderKey::Custom(s_trimmed.to_string())),
}
}
}
impl PartialEq for HeaderKey {
fn eq(&self, other: &Self) -> bool {
self.as_str().to_ascii_lowercase() == other.as_str().to_ascii_lowercase()
}
}
impl Eq for HeaderKey {}
impl Hash for HeaderKey {
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_str().to_ascii_lowercase().hash(state);
}
}
};
}
define_header_keys! {
CacheControl => "Cache-Control",
Connection => "Connection",
Date => "Date",
Pragma => "Pragma",
Trailer => "Trailer",
TransferEncoding => "Transfer-Encoding",
Upgrade => "Upgrade",
Via => "Via",
Warning => "Warning",
Accept => "Accept",
AcceptCharset => "Accept-Charset",
AcceptEncoding => "Accept-Encoding",
AcceptLanguage => "Accept-Language",
Authorization => "Authorization",
Cookie => "Cookie",
Expect => "Expect",
From => "From",
Host => "Host",
IfMatch => "If-Match",
IfModifiedSince => "If-Modified-Since",
IfNoneMatch => "If-None-Match",
IfRange => "If-Range",
IfUnmodifiedSince => "If-Unmodified-Since",
MaxForwards => "Max-Forwards",
Origin => "Origin",
Range => "Range",
Referer => "Referer",
TE => "TE",
UserAgent => "User-Agent",
AcceptRanges => "Accept-Ranges",
Age => "Age",
ETag => "ETag",
Location => "Location",
ProxyAuthenticate => "Proxy-Authenticate",
RetryAfter => "Retry-After",
Server => "Server",
SetCookie => "Set-Cookie",
Vary => "Vary",
WWWAuthenticate => "WWW-Authenticate",
Allow => "Allow",
ContentEncoding => "Content-Encoding",
ContentLanguage => "Content-Language",
ContentLength => "Content-Length",
ContentLocation => "Content-Location",
ContentRange => "Content-Range",
ContentType => "Content-Type",
Expires => "Expires",
LastModified => "Last-Modified",
AccessControlAllowCredentials => "Access-Control-Allow-Credentials",
AccessControlAllowHeaders => "Access-Control-Allow-Headers",
AccessControlAllowMethods => "Access-Control-Allow-Methods",
AccessControlAllowOrigin => "Access-Control-Allow-Origin",
AccessControlExposeHeaders => "Access-Control-Expose-Headers",
AccessControlMaxAge => "Access-Control-Max-Age",
SecFetchDest => "Sec-Fetch-Dest",
SecFetchMode => "Sec-Fetch-Mode",
SecFetchSite => "Sec-Fetch-Site",
SecFetchUser => "Sec-Fetch-User",
SecWebSocketAccept => "Sec-WebSocket-Accept",
SecWebSocketExtensions => "Sec-WebSocket-Extensions",
SecWebSocketKey => "Sec-WebSocket-Key",
SecWebSocketProtocol => "Sec-WebSocket-Protocol",
SecWebSocketVersion => "Sec-WebSocket-Version",
Forwarded => "Forwarded",
XForwardedFor => "X-Forwarded-For",
XForwardedHost => "X-Forwarded-Host",
XForwardedProto => "X-Forwarded-Proto",
DNT => "DNT",
KeepAlive => "Keep-Alive",
UpgradeInsecureRequests => "Upgrade-Insecure-Requests",
}
impl From<&str> for HeaderKey {
fn from(s: &str) -> Self {
Self::from_str(s).unwrap_or(HeaderKey::Custom(s.to_string()))
}
}
impl From<String> for HeaderKey {
fn from(s: String) -> Self {
Self::from_str(&s).unwrap_or(HeaderKey::Custom(s))
}
}
impl fmt::Display for HeaderKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Debug, Clone)]
pub struct Headers(AHashMap<HeaderKey, String>);
impl Headers {
pub fn new() -> Self {
Self(AHashMap::with_capacity(16))
}
pub fn with(mut self, key: HeaderKey, value: impl Into<String>) -> Self {
self.0.insert(key, value.into());
self
}
pub fn insert(&mut self, key: HeaderKey, value: impl Into<String>) -> Option<String> {
self.0.insert(key, value.into())
}
pub fn get(&self, key: &HeaderKey) -> Option<&String> {
self.0.get(key)
}
pub fn remove(&mut self, key: &HeaderKey) -> Option<String> {
self.0.remove(key)
}
pub fn contains(&self, key: &HeaderKey) -> bool {
self.0.contains_key(key)
}
}
impl Deref for Headers {
type Target = AHashMap<HeaderKey, String>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<AHashMap<HeaderKey, String>> for Headers {
fn from(map: AHashMap<HeaderKey, String>) -> Self {
Self(map)
}
}
impl From<Headers> for AHashMap<HeaderKey, String> {
fn from(headers: Headers) -> Self {
headers.0
}
}
impl FromIterator<(HeaderKey, String)> for Headers {
fn from_iter<I: IntoIterator<Item = (HeaderKey, String)>>(iter: I) -> Self {
Self(AHashMap::from_iter(iter))
}
}
impl<'a> IntoIterator for &'a Headers {
type Item = (&'a HeaderKey, &'a String);
type IntoIter = <&'a AHashMap<HeaderKey, String> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
impl IntoIterator for Headers {
type Item = (HeaderKey, String);
type IntoIter = <AHashMap<HeaderKey, String> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}