ask_nicely 0.2.0

A rest client helper library.
Documentation
use crate::api_response::ApiError;
use crate::authentication::Authentication;
use crate::mutate_req::MutateReq;
use std::str::FromStr;


/// An asynchronous `Client` to make Requests with.
///
/// The Client has various configuration values to tweak, but the defaults
/// are set to what is usually the most commonly desired value. To configure a
/// `Client`, use `Client::builder()`.
///
/// The `Client` holds a connection pool internally, so it is advised that
/// you create one and **reuse** it.
///
/// You do **not** have to wrap the `ApiClient` in an [`Rc`] or [`Arc`] to **reuse** it,
/// because it already uses an [`Arc`] internally.
///
/// [`Rc`]: std::rc::Rc
pub struct ApiClient<'a> {
    pub base_url: &'a str,
    pub auth: Authentication<'a>,
    pub client: reqwest::Client,
}

pub trait CanApiClient<'a> {
    fn get_auth(&'a self) -> &'a Authentication<'a>;
    fn get_base_url(&self) -> String;
    fn get_client(&'a self) -> &'a reqwest::Client;

    fn request<'q, REQ>(
        &'a self,
        call: &'a REQ,
    ) -> impl futures::Future<Output = Result<Result<REQ::Response, ApiError>, crate::errors::Error>>
    where
        REQ: crate::request::Request<'a>,
    {
        async {
            let api_call = call.to_api_call();

            let url =
                reqwest::Url::from_str(self.get_base_url().trim())?.join(api_call.path.as_ref())?;

            let mut request = self.get_client().request(api_call.method.clone(), url);

            request = api_call.request.mutate_req(request);

            request = (self.get_auth(), api_call.authed).mutate_req(request);

            let response = request.send().await?;

            let status = response.status();

            if status.is_success() {
                return Ok(Ok(response.json::<REQ::Response>().await?));
            }

            Ok(Err(ApiError::new(status, response.text().await?)))
        }
    }
}

impl<'a> CanApiClient<'a> for ApiClient<'a> {
    fn get_auth(&'a self) -> &'a Authentication<'a> {
        &self.auth
    }

    fn get_base_url(&self) -> String {
        self.base_url.to_string()
    }

    fn get_client(&'a self) -> &'a reqwest::Client {
        &self.client
    }
}

impl<'a> ApiClient<'a> {
    pub fn builder(base_url: &'a str) -> crate::client_builder::ApiClientBuilder<'a> {
        crate::client_builder::ApiClientBuilder::new(base_url)
    }

    pub fn new(base_url: &'a str, auth: Authentication<'a>) -> Self {
        let client = reqwest::Client::new();

        Self {
            base_url,
            auth,
            client,
        }
    }
}