palpo-core 0.0.2

Core library used by the palpo crates.
Documentation
use std::future::Future;
use std::ops::{Deref, DerefMut};

use reqwest::{Client as ReqwestClient, ClientBuilder, Request as ReqwestRequest};
use salvo::http::header::CONTENT_TYPE;
use salvo::http::{HeaderName, HeaderValue, Method};
use serde::Deserialize;
use thiserror::Error;
use url::{ParseError, Url};

pub fn client() -> ReqwestClient {
    ClientBuilder::new().build().unwrap()
}

#[derive(Debug)]
pub struct SendRequest {
    inner: ReqwestRequest,
}

#[macro_export]
macro_rules! json_body_modifier {
    ($name:ident) => {
        impl crate::sending::SendModifier for $name {
            fn modify(self, request: &mut crate::sending::SendRequest) -> Result<(), crate::sending::SendError> {
                let bytes = serde_json::to_vec(&self)?;
                *request.body_mut() = Some(bytes.into());
                Ok(())
            }
        }
    };
}
macro_rules! method {
    ($name:ident, $method:ident) => {
        pub fn $name(url: Url) -> SendRequest {
            SendRequest {
                inner: ReqwestRequest::new(Method::$method, url),
            }
        }
    };
}
method!(get, GET);
method!(patch, PATCH);
method!(put, PUT);
method!(post, POST);
method!(delete, DELETE);

#[derive(Error, Debug)]
pub enum SendError {
    #[error("parse url: `{0}`")]
    Url(#[from] ParseError),
    #[error("reqwest: `{0}`")]
    Reqwest(#[from] reqwest::Error),
    #[error("json: `{0}`")]
    Json(#[from] serde_json::Error),
    #[error("other: `{0}`")]
    Other(String),
}

impl SendError {
    pub fn other(msg: impl Into<String>) -> Self {
        Self::Other(msg.into())
    }
}

pub type SendResult<T> = Result<T, SendError>;

impl SendRequest {
    method!(get, GET);
    method!(patch, PATCH);
    method!(put, PUT);
    method!(post, POST);
    method!(delete, DELETE);

    pub fn into_inner(self) -> reqwest::Request {
        self.inner
    }
    pub fn stuff(mut self, modifier: impl SendModifier) -> Result<Self, SendError> {
        modifier.modify(&mut self)?;
        if !self.headers().contains_key(CONTENT_TYPE) {
            self.headers_mut()
                .insert(CONTENT_TYPE, "application/json".parse().unwrap());
        }
        Ok(self)
    }

    pub async fn load<R>(self) -> Result<R, SendError>
    where
        R: for<'de> Deserialize<'de>,
    {
        let res = client().execute(self.inner).await?;
        res.json().await.map_err(SendError::Reqwest)
    }

    pub async fn load_by_client<R>(self, client: ReqwestClient) -> Result<R, SendError>
    where
        R: for<'de> Deserialize<'de>,
    {
        let res = client.execute(self.inner).await?;
        res.json().await.map_err(SendError::Reqwest)
    }

    pub async fn send<R>(self) -> Result<R, SendError>
    where
        R: for<'de> Deserialize<'de>,
    {
        let res = client().execute(self.inner).await?;
        res.json().await.map_err(SendError::Reqwest)
    }

    pub async fn send_by_client<R>(self, client: ReqwestClient) -> Result<R, SendError>
    where
        R: for<'de> Deserialize<'de>,
    {
        let res = client.execute(self.inner).await?;
        res.json().await.map_err(SendError::Reqwest)
    }

    pub fn exec(self) -> impl Future<Output = Result<reqwest::Response, reqwest::Error>> {
        client().execute(self.inner)
    }
}

impl Deref for SendRequest {
    type Target = ReqwestRequest;
    fn deref(&self) -> &Self::Target {
        &self.inner
    }
}
impl DerefMut for SendRequest {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.inner
    }
}

pub trait SendModifier {
    fn modify(self, request: &mut SendRequest) -> Result<(), SendError>;
}

impl SendModifier for (HeaderName, HeaderValue) {
    fn modify(self, request: &mut SendRequest) -> Result<(), SendError> {
        request.headers_mut().append(self.0, self.1);
        Ok(())
    }
}