use crate::enums::char_set::CharSet;
use crate::enums::content_encoding::ContentEncoding;
use crate::enums::header::content_type::ContentType;
use crate::enums::header::http_header::HttpHeader;
use crate::enums::http_error::HttpError;
use crate::enums::transfer_encoding::TransferEncoding;
use crate::utils::{http_header_field_name, parse_util};
use async_regex::CRLF;
use core::fmt;
use futures::{AsyncBufRead, AsyncBufReadExt};
use std::collections::HashMap;
use std::collections::hash_map::{IntoIter, Iter, Values, ValuesMut};
use std::fmt::{Display, Formatter};
use std::str::FromStr;
#[derive(Clone, Default)]
pub struct HttpHeaderList(HashMap<String, HttpHeader>);
impl HttpHeaderList {
pub fn add(&mut self, header: HttpHeader) {
self.0
.insert(header.field_name_lowercase().into_owned(), header);
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn contains(&mut self, key: &str) -> bool {
self.0.contains_key(key)
}
pub fn merge_list<T>(&mut self, list: T)
where
T: IntoIterator<Item = (String, HttpHeader)>,
{
for (key, value) in list {
self.0.insert(key, value);
}
}
pub fn merge_items<T>(&mut self, items: T)
where
T: IntoIterator<Item = HttpHeader>,
{
for item in items {
self.add(item);
}
}
pub fn values(&self) -> Values<'_, String, HttpHeader> {
self.0.values()
}
pub fn get(&self, key: &str) -> Option<&HttpHeader> {
self.0.get(key)
}
pub fn iter(&self) -> Iter<'_, String, HttpHeader> {
self.0.iter()
}
pub async fn read(mut reader: impl AsyncBufRead + Unpin) -> Result<HttpHeaderList, HttpError> {
let mut headers = HttpHeaderList::default();
let mut line: String = String::new();
loop {
line.clear();
reader.read_line(&mut line).await?;
if line.len() == 2 && line.as_bytes() == CRLF {
break;
}
headers.add(HttpHeader::from_str(&line[..line.len() - 2])?);
}
Ok(headers)
}
pub fn get_charset(&self, default: CharSet) -> CharSet {
match self.0.get(http_header_field_name::CONTENT_TYPE_L) {
Some(header) => match header {
HttpHeader::ContentType(content_types) => {
match content_types.iter().find_map(|c_type| match c_type {
ContentType::Charset(charset) => Some(charset),
_ => None,
}) {
Some(charset) => charset.clone(),
_ => default,
}
}
_ => unreachable!(),
},
None => default,
}
}
pub fn get_content_encoding(&self) -> &[ContentEncoding] {
match self.0.get(http_header_field_name::CONTENT_ENCODING_L) {
Some(header) => match header {
HttpHeader::ContentEncoding(encoding) => encoding,
_ => unreachable!(),
},
None => &[],
}
}
pub fn get_content_length(&self) -> Option<usize> {
match self.0.get(http_header_field_name::CONTENT_LENGTH_L) {
Some(HttpHeader::ContentLength(length)) => Some(*length),
_ => None,
}
}
pub fn get_multipart_data(&self) -> (bool, String, String) {
match self.0.get(http_header_field_name::CONTENT_TYPE_L) {
Some(HttpHeader::ContentType(content_types)) => {
parse_util::get_multipart_data(content_types)
}
_ => (false, String::new(), String::new()),
}
}
pub fn get_transfer_encodings(&self) -> &[TransferEncoding] {
match self.0.get(http_header_field_name::TRANSFER_ENCODING_L) {
Some(HttpHeader::TransferEncoding(transfer_encoding)) => transfer_encoding,
_ => &[],
}
}
}
impl<'a> IntoIterator for &'a HttpHeaderList {
type Item = &'a HttpHeader;
type IntoIter = Values<'a, String, HttpHeader>;
fn into_iter(self) -> Self::IntoIter {
self.0.values()
}
}
impl<'a> IntoIterator for &'a mut HttpHeaderList {
type Item = &'a mut HttpHeader;
type IntoIter = ValuesMut<'a, String, HttpHeader>;
fn into_iter(self) -> Self::IntoIter {
self.0.values_mut()
}
}
impl IntoIterator for HttpHeaderList {
type Item = (String, HttpHeader);
type IntoIter = IntoIter<String, HttpHeader>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<T> From<T> for HttpHeaderList
where
T: IntoIterator<Item = HttpHeader>,
{
fn from(headers: T) -> Self {
let mut result = HttpHeaderList::default();
for header in headers {
result.add(header);
}
result
}
}
impl fmt::Display for HttpHeaderList {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let mut first = true;
for (_, header) in self.iter() {
if !first {
f.write_str("\r\n")?;
}
write!(f, "{}", header)?;
first = false;
}
Ok(())
}
}
impl fmt::Debug for HttpHeaderList {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(self, f)
}
}