etebase 0.6.1

A Rust client library for Etebase
Documentation
use reqwest::{
    blocking::{Client as ReqwestClient, RequestBuilder},
    header,
    redirect::Policy,
};

use crate::error::{Error, Result};

use super::client_impl::{ClientImplementation, Response};

static APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),);

impl From<reqwest::Error> for Error {
    fn from(err: reqwest::Error) -> Error {
        if err.is_builder() || err.is_timeout() || err.is_redirect() {
            Error::Generic(err.to_string())
        } else {
            Error::Connection(err.to_string())
        }
    }
}

pub(super) struct Client {
    req_client: ReqwestClient,
}

impl Client {
    pub fn new(client_name: &str) -> Result<Self> {
        let req_client = ReqwestClient::builder()
            .user_agent(format!("{} {}", client_name, APP_USER_AGENT))
            .redirect(Policy::none())
            .build()?;

        Ok(Self { req_client })
    }

    fn with_auth_header(
        &self,
        builder: RequestBuilder,
        auth_token: Option<&str>,
    ) -> RequestBuilder {
        match auth_token {
            Some(auth_token) => {
                builder.header(header::AUTHORIZATION, format!("Token {}", auth_token))
            }
            None => builder,
        }
    }

    fn with_base_headers(&self, builder: RequestBuilder) -> RequestBuilder {
        builder
            .header(header::CONTENT_TYPE, "application/msgpack")
            .header(header::ACCEPT, "application/msgpack")
    }

    fn prep_client(&self, builder: RequestBuilder, auth_token: Option<&str>) -> RequestBuilder {
        self.with_base_headers(self.with_auth_header(builder, auth_token))
    }

    fn get_inner(&self, url: &str, auth_token: Option<&str>) -> Result<Response> {
        let req = self.prep_client(self.req_client.get(url), auth_token);
        let resp = req.send()?;
        let status = resp.status().as_u16();
        let ret = Response::new(resp.bytes()?.to_vec(), status);
        Ok(ret)
    }

    fn post_inner(&self, url: &str, auth_token: Option<&str>, body: Vec<u8>) -> Result<Response> {
        let req = self
            .prep_client(self.req_client.post(url), auth_token)
            .body(body);
        let resp = req.send()?;
        let status = resp.status().as_u16();
        let ret = Response::new(resp.bytes()?.to_vec(), status);
        Ok(ret)
    }

    fn put_inner(&self, url: &str, auth_token: Option<&str>, body: Vec<u8>) -> Result<Response> {
        let req = self
            .prep_client(self.req_client.put(url), auth_token)
            .body(body);
        let resp = req.send()?;
        let status = resp.status().as_u16();
        let ret = Response::new(resp.bytes()?.to_vec(), status);
        Ok(ret)
    }

    fn patch_inner(&self, url: &str, auth_token: Option<&str>, body: Vec<u8>) -> Result<Response> {
        let req = self
            .prep_client(self.req_client.patch(url), auth_token)
            .body(body);
        let resp = req.send()?;
        let status = resp.status().as_u16();
        let ret = Response::new(resp.bytes()?.to_vec(), status);
        Ok(ret)
    }

    fn delete_inner(&self, url: &str, auth_token: Option<&str>) -> Result<Response> {
        let req = self.prep_client(self.req_client.delete(url), auth_token);
        let resp = req.send()?;
        let status = resp.status().as_u16();
        let ret = Response::new(resp.bytes()?.to_vec(), status);
        Ok(ret)
    }
}

impl ClientImplementation for Client {
    fn get(&self, url: &str, auth_token: Option<&str>) -> Response {
        match self.get_inner(url, auth_token) {
            Ok(resp) => resp,
            Err(err) => Response::new_err(err),
        }
    }

    fn post(&self, url: &str, auth_token: Option<&str>, body: Vec<u8>) -> Response {
        match self.post_inner(url, auth_token, body) {
            Ok(resp) => resp,
            Err(err) => Response::new_err(err),
        }
    }

    fn put(&self, url: &str, auth_token: Option<&str>, body: Vec<u8>) -> Response {
        match self.put_inner(url, auth_token, body) {
            Ok(resp) => resp,
            Err(err) => Response::new_err(err),
        }
    }

    fn patch(&self, url: &str, auth_token: Option<&str>, body: Vec<u8>) -> Response {
        match self.patch_inner(url, auth_token, body) {
            Ok(resp) => resp,
            Err(err) => Response::new_err(err),
        }
    }

    fn delete(&self, url: &str, auth_token: Option<&str>) -> Response {
        match self.delete_inner(url, auth_token) {
            Ok(resp) => resp,
            Err(err) => Response::new_err(err),
        }
    }
}