simplist 0.0.5

plain and simple http, for when you just want to make a darn request! supports tokio-based async, traditional sync and async-await models.
Documentation
use std::borrow::Cow;

use hyper::Body;
use hyper::Headers;
use hyper::Method;
use hyper::Request;
use hyper::Uri;
use hyper::header::Raw;

use simplist::HttpContent;
use simplist::HttpMethod;
use simplist::Url;



/// a http request.
///
/// this struct allows you to customize specific details in a http request, and let you use a non-standard http method,
/// set http headers, or read and manipulate the url bit-by-bit.
///
/// in general, using the provided `fn get()`, `fn post()`, `fn delete()` in `HttpClient` will be much simpler.
///
/// # examples
///
/// ```
/// use simplist::HttpClient;
/// use simplist::HttpMethod;
/// use simplist::HttpRequest;
///
/// let client = HttpClient::new(...);
/// let custom = HttpRequest::new()
///     .with_url("https://hinaria.com".parse()?)
///     .with_method(HttpMethod::Get);
///
/// let response = await client.request(custom)?;
/// let string   = response.read_as_string()?;
///
/// println!("{:?}", string);
/// // => Ok("<!doctype html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"utf-8\"> ...")
/// ```
#[derive(Debug)]
pub struct HttpRequest {
    url:        Url,
    underlying: Request<Body>,
}

impl HttpRequest {
    /// creates a new, empty http request.
    ///
    /// # examples
    ///
    /// ```
    /// use simplist::HttpRequest;
    ///
    /// let request = HttpRequest::new();
    /// ```
    pub fn new() -> HttpRequest {
        let url        = Url::default();
        let underlying = Request::new(Method::Get, Uri::default());

        HttpRequest { url, underlying }
    }

    /// creates a new http request for the specified `url` and `method`.
    ///
    /// # examples
    ///
    /// ```
    /// use simplist::HttpMethod;
    /// use simplist::HttpRequest;
    ///
    /// let url     = "https://hinaria.com".parse()?;
    /// let method  = HttpMethod::Get;
    /// let request = HttpRequest::with(url, method);
    /// ```
    pub fn with(url: Url, method: HttpMethod) -> HttpRequest {
        HttpRequest::new()
            .with_url   (url)
            .with_method(method)
    }

    /// returns the url for this request.
    ///
    /// # examples
    ///
    /// ```
    /// use simplist::HttpMethod;
    /// use simplist::HttpRequest;
    ///
    /// let url     = "https://hinaria.com".parse()?;
    /// let request = HttpRequest::with(url.clone(), HttpMethod::Get);
    ///
    /// assert_eq!(request.url(), url);
    /// ```
    pub fn url(&self) -> &Url {
        &self.url
    }

    /// returns the method for this request.
    ///
    /// # examples
    ///
    /// ```
    /// use simplist::HttpMethod;
    /// use simplist::HttpRequest;
    ///
    /// let request = HttpRequest::with("https://hinaria.com", HttpMethod::Get);
    ///
    /// assert_eq!(request.method(), HttpMethod::Get);
    /// ```
    pub fn method(&self) -> HttpMethod {
        let copy = self.underlying.method().clone();
        Into::into(copy)
    }

    /// returns the url path for this request.
    ///
    /// # examples
    ///
    /// ```
    /// use simplist::HttpMethod;
    /// use simplist::HttpRequest;
    ///
    /// let url     = "http://hinaria.com/hello/index.html?q=search".parse()?;
    /// let request = HttpRequest::with(url, HttpMethod::Get);
    ///
    /// assert_eq!(request.path(), "/hello/index.html");
    /// ```
    pub fn path(&self) -> &str {
        self.underlying.path()
    }

    /// returns the url query for this request.
    ///
    /// # examples
    ///
    /// ```
    /// use simplist::HttpMethod;
    /// use simplist::HttpRequest;
    ///
    /// let url     = "http://hinaria.com/hello/index.html?q=search".parse()?;
    /// let request = HttpRequest::with(url, HttpMethod::Get);
    ///
    /// assert_eq!(request.query(), Some("q=search"));
    /// ```
    pub fn query(&self) -> Option<&str> {
        self.underlying.query()
    }

    /// returns the headers for this request.
    ///
    /// # examples
    ///
    /// ```
    /// use simplist::HttpMethod;
    /// use simplist::HttpRequest;
    ///
    /// let request = HttpRequest::with("https://hinaria.com", HttpMethod::Get);
    /// let headers = request.headers();
    ///
    /// assert_eq!(headers.has::<ContentType>(), true);
    /// ```
    pub fn headers(&self) -> &Headers {
        self.underlying.headers()
    }

    /// returns a mutable reference to the headers for this request.
    ///
    /// # examples
    ///
    /// ```
    /// use hyper::headers::Basic;
    /// use simplist::HttpMethod;
    /// use simplist::HttpRequest;
    ///
    /// let request = HttpRequest::with("https://hinaria.com", HttpMethod::Get);
    /// let headers = request.headers_mut();
    ///
    /// headers.set(Basic {
    ///     username: "annie".to_owned(),
    ///     password: None,
    /// });
    /// ```
    pub fn headers_mut(&mut self) -> &mut Headers {
        self.underlying.headers_mut()
    }

    /// sets this request's method to `method`
    ///
    /// # examples
    ///
    /// ```
    /// use simplist::HttpMethod;
    /// use simplist::HttpRequest;
    ///
    /// HttpRequest::new()
    ///     .with_url   ("https://hinaria.com".parse()?)
    ///     .with_method(HttpMethod::Get);
    /// ```
    pub fn with_method(mut self, method: HttpMethod) -> HttpRequest {
        self.underlying.set_method(method.into());
        self
    }

    /// sets this request's url to `url`
    ///
    /// # examples
    ///
    /// ```
    /// use simplist::HttpMethod;
    /// use simplist::HttpRequest;
    ///
    /// HttpRequest::new()
    ///     .with_url   ("https://hinaria.com".parse()?)
    ///     .with_method(HttpMethod::Get);
    /// ```
    pub fn with_url(mut self, url: Url) -> HttpRequest {
        self.url = url;
        self
    }

    /// attaches `body` to this request.
    ///
    /// # examples
    ///
    /// ## sending a json payload.
    ///
    /// ```
    /// use hyper::header::ContentType;
    /// use simplist::HttpMethod;
    /// use simplist::HttpRequest;
    ///
    /// HttpRequest::new()
    ///     .with_url   ("https://api.hinaria.com/users/@me/database".parse()?)
    ///     .with_method(HttpMethod::Put)
    ///     .with_header(ContentType::json())
    ///     .with_body  ("{ id: 1234 }");
    /// ```
    ///
    /// ## sending an octet stream.
    ///
    /// ```
    /// use hyper::header::ContentType;
    /// use simplist::HttpMethod;
    /// use simplist::HttpRequest;
    ///
    /// let data: Vec<u8> = ...;
    ///
    /// HttpRequest::new()
    ///     .with_url   ("https://api.hinaria.com/users/@me/database".parse()?)
    ///     .with_method(HttpMethod::Put)
    ///     .with_header(ContentType::octet_stream())
    ///     .with_body  (data);
    /// ```
    pub fn with_body<TBody>(mut self, body: Option<TBody>) -> HttpRequest
        where TBody: Into<HttpContent> {

        let content: HttpContent = body.into();
        let content: Body        = content.into();

        self.underlying.set_body(content);
        self
    }

    /// sets the header `key` to `value` in this request.
    ///
    /// `body` can be any object which can be converted to a [`HttpContent`](struct.HttpContent.html). for more information, see [`HttpContent`](struct.HttpContent.html).
    ///
    /// # examples
    ///
    /// ```
    /// use simplist::HttpMethod;
    /// use simplist::HttpRequest;
    ///
    /// let request = HttpRequest::with("https://hinaria.com", HttpMethod::Get)
    ///     .with_header(Basic { username: "annie".to_owned(), password: None });
    ///     .with_header(ContentType::json())
    ///     .with_header(Referer::new("mozilla.org"))
    /// ```
    pub fn with_header<TKey, TValue>(mut self, key: TKey, value: TValue) -> HttpRequest
        where TKey:   Into<Cow<'static, str>>,
              TValue: Into<Raw>, {

        {
            let headers = self.underlying.headers_mut();
            let key     = key.into();

            headers.remove_raw(&key);
            headers.append_raw(key, value);
        }

        self
    }
}

impl Into<Request<Body>> for HttpRequest {
    fn into(self) -> Request<Body> {
         let mut request = self.underlying;

         request.set_uri(self.url.into());
         request
    }
}