httpio 0.2.4

A transport-agnostic, async HTTP/1.1 client library for any runtime.
Documentation
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)
    }
}