httpclient 0.26.1

HTTP client with middleware. Middleware provides composable support for record/replay, logging, exponential backoff, and more.
Documentation
use http::{header, HeaderMap, HeaderValue};
use http::header::{AsHeaderName, CONTENT_TYPE, IntoHeaderName};
use crate::{InMemoryBody, InMemoryRequest, multipart};
use crate::multipart::WriteBytes;
use crate::multipart::form::Form;

impl<T: WriteBytes> WriteBytes for Part<T> {
    fn write(self, buf: &mut Vec<u8>) {
        multipart::write_headers(buf, &self.headers);
        self.body.write(buf);
    }
}

#[derive(Debug)]
pub struct Part<B> {
    pub headers: HeaderMap,
    pub body: B,
}

impl<B> Part<B> {
    pub fn new(headers: HeaderMap, body: B) -> Self {
        Part { headers, body }
    }

    #[must_use]
    pub fn content_id(mut self, id: &str) -> Self {
        self.headers.insert("Content-ID", id.parse().expect("Unable to parse content id"));
        self
    }

    pub fn header_str<H: AsHeaderName>(&self, h: H) -> Option<&str> {
        self.headers.get(h).and_then(|v| v.to_str().ok())
    }

    pub fn header<H: IntoHeaderName>(mut self, h: H, v: HeaderValue) -> Self {
        self.headers.insert(h, v);
        self
    }

}

impl Part<InMemoryRequest> {
    pub fn request(body: InMemoryRequest) -> Self {
        let mut headers = HeaderMap::new();
        headers.insert(CONTENT_TYPE, "application/http".parse().expect("Unable to parse content type"));
        Part { headers, body }
    }
}

impl Part<InMemoryBody> {
    pub fn text(body: String) -> Self {
        let mut headers = HeaderMap::new();
        headers.insert(header::CONTENT_TYPE, "text/plain".parse().expect("Unable to parse content type"));
        Part { headers, body: InMemoryBody::Text(body) }
    }

    pub fn html(body: String) -> Self {
        let mut headers = HeaderMap::new();
        headers.insert(header::CONTENT_TYPE, "text/html".parse().expect("Unable to parse content type"));
        Part { headers, body: InMemoryBody::Text(body) }
    }

    pub fn form(form: Form<InMemoryBody>) -> Self {
        let mut headers = HeaderMap::new();
        headers.insert(CONTENT_TYPE, form.full_content_type().parse().expect("Unable to parse content type"));
        let body: Vec<u8> = form.into();
        let body = match String::from_utf8(body) {
            Ok(s) => InMemoryBody::Text(s),
            Err(e) => {
                InMemoryBody::Bytes(e.into_bytes())
            }
        };
        Part { headers, body }
    }
}

impl<T: Default> Default for Part<T> {
    fn default() -> Self {
        Part::new(HeaderMap::new(), T::default())
    }
}

impl<T: WriteBytes> From<Part<T>> for Vec<u8> {
    fn from(value: Part<T>) -> Self {
        let mut buf = Vec::new();
        value.write(&mut buf);
        buf
    }
}

impl Into<InMemoryBody> for Part<InMemoryBody> {
    fn into(self) -> InMemoryBody {
        let mut buf = Vec::new();
        self.write(&mut buf);
        match String::from_utf8(buf) {
            Ok(s) => InMemoryBody::Text(s),
            Err(e) => InMemoryBody::Bytes(e.into_bytes())
        }
    }
}