qx_rs_req 0.0.0

quick request util in rust.
Documentation
use qx_rs_err::err::*;

use std::{collections::HashMap, fmt::Debug};
use reqwest::{self, header::CONTENT_TYPE};

#[derive(Debug, Clone)]
pub enum Method {
    Get,
    Post,
    Put,
    Patch,
    Delete,
    Copy,
    Head,
    Options,
    Link,
    Unlink,
    Purge,
    Lock,
    Unlock,
    PropFind,
    View,
    Other(&'static str),
}

#[derive(Debug, Clone)]
pub enum Body<'a> {
    ApplicationJson(&'a str),
    XwwwFormUrlEncoded(HashMap<String, String>),
    MultipartFormData(HashMap<String, MultipartFormValue<'a>>),
    ApplicationXml(&'a str),
    ApplicationJavascript(&'a str),
    TextPlain(&'a str),
    TextHtml(&'a str),
    Other(&'a str, &'a str),
    Raw(&'a str),
}

pub struct Request<'a> {
    pub url: &'a str,
    pub method: Method,
    pub querys: Option<HashMap<String, String>>,
    pub headers: Option<HashMap<String, String>>,
    pub body: Option<Body<'a>>,
}
impl <'a> Request<'a> {
    pub fn new(url: &'a str, method: Method) -> Self {
        Request { url, method, headers: None, querys: None, body: None }
    }
    pub fn headers(mut self, headers: HashMap<String, String>) -> Self {
        self.headers = Some(headers);
        self
    }
    pub fn querys(mut self, querys: HashMap<String, String>) -> Self {
        self.querys = Some(querys);
        self
    }
    pub fn body(mut self, body: Body<'a>) -> Self {
        self.body = Some(body);
        self
    }
}
impl <'a> Request<'a> {
    pub async fn fetch_bytes(&self) -> Result<bytes::Bytes> {
        let resp = _fetch(self).await?;
        let bytes = resp.bytes().await.map_err(|err| {
            Error::error(Box::new(err))
        })?;
        Ok(bytes)
    }
    pub async fn fetch_text(&self) -> Result<String> {
        let resp = _fetch(self).await?;
        let text = resp.text().await.map_err(|err| {
            Error::error(Box::new(err))
        })?;
        Ok(text)
    }
}

pub fn request<'a>(url: &'a str, method: Method) -> Request<'a> {
    Request::new(url, method)
}

#[derive(Debug, Clone)]
pub enum MultipartFormValue<'a> {
    Text(&'a str),
    File(MultipartFormFile<'a>)    
}

#[derive(Debug, Clone)]
pub struct MultipartFormFile<'a> {
    pub file_name: &'a str,
    pub mine: &'a str,
    pub file_path: &'a str,
}
impl <'a> MultipartFormFile<'a> {
    pub fn new(file_name: &'a str, mine: &'a str, file_path: &'a str) -> Self {
        Self {
            file_name,
            mine,
            file_path
        }
    }
}

fn _method<'a>(method: Method) -> &'a str {
    match method {
        Method::Get => "GET",
        Method::Post => "POST",
        Method::Put => "PUT",
        Method::Patch => "PATCH",
        Method::Delete => "DELETE",
        Method::Copy => "COPY",
        Method::Head => "HEAD",
        Method::Options => "OPTIONS",
        Method::Link => "LINK",
        Method::Unlink => "UNLINK",
        Method::Purge => "PURGE",
        Method::Lock => "LOCK",
        Method::Unlock => "UNLOCK",
        Method::PropFind => "PROPFIND",
        Method::View => "VIEW",
        Method::Other(m) => m,
    }
}
async fn _fetch<'a>(request: &Request<'a>) -> Result<reqwest::Response> {
    let client = reqwest::Client::builder().build().map_err(|err| {
        Error::error(Box::new(err))
    })?;
    let url = reqwest::Url::parse(request.url).map_err(|err| {
        Error::error(Box::new(err))
    })?;
    let method = reqwest::Method::from_bytes(_method(request.method.clone()).as_bytes()).map_err(|err| {
        Error::error(Box::new(err))
    })?;
    let mut req = client.request(method, url);
    if let Some(hs) = &request.headers {
        for (k, v) in hs {
            req = req.header(k, v);
        }
    }
    if let Some(b) = &request.body {
        match b {
            Body::ApplicationJson(json) => {
                req = req.header(CONTENT_TYPE, "application/json");
                req = req.body(json.to_string());
            },
            Body::XwwwFormUrlEncoded(f) => {
                // automaticly inject headers
                req = req.form(&f);
            },
            Body::MultipartFormData(ms) => {
                // automatically‌ inject headers
                let form = _multipart_form(ms).await?;
                req = req.multipart(form);
            },
            Body::ApplicationXml(xml) => {
                req = req.header(CONTENT_TYPE, "application/xml");
                req = req.body(xml.to_string());
            },
            Body::ApplicationJavascript(js) => {
                req = req.header(CONTENT_TYPE, "application/javascript");
                req = req.body(js.to_string());
            },
            Body::TextPlain(t) => {
                req = req.header(CONTENT_TYPE, "text/plain");
                req = req.body(t.to_string());
            },
            Body::TextHtml(h) => {
                req = req.header(CONTENT_TYPE, "text/html");
                req = req.body(h.to_string());
            },
            Body::Other(h, t) => {
                req = req.header(CONTENT_TYPE, h.to_string());
                req = req.body(t.to_string());
            },
            Body::Raw(t) => {
                // no inject headers
                req = req.body(t.to_string());
            },
        }
    }
    let resp = req.send().await.map_err(|err| {
        Error::error(Box::new(err))
    })?;
    Ok(resp)
}
async fn _multipart_form<'a>(multipart_form: &HashMap<String, MultipartFormValue<'a>>) -> Result<reqwest::multipart::Form> {
    let mut form = reqwest::multipart::Form::new();
    for (k, v) in multipart_form {
        let k = k.clone();
        match v {
            MultipartFormValue::Text(t) => {
                form = form.text(k, t.to_string());
            },
            MultipartFormValue::File(f) => {
                let mut p = reqwest::multipart::Part::file(f.file_path.to_string()).await.map_err(|err| {
                    Error::error(Box::new(err))
                })?;
                p = p.file_name(f.file_name.to_string());
                p = p.mime_str(f.mine).map_err(|err| {
                    Error::error(Box::new(err))
                })?;
                form = form.part(k, p);
            },
        }
    }
    Ok(form)
}