eve-esi-api 0.0.4

This library provides an authentication to Eve-esi API and some endpoints to call.
Documentation
use std::collections::HashMap;
use std::sync::Arc;

use log::*;
use oauth2::TokenResponse;
use reqwest::Client;
use reqwest::Response;
use reqwest::StatusCode;
use serde::de::DeserializeOwned;

use crate::Authent;

use crate::errors::EveEsiError;
use crate::Result;

///
/// Client used in api calls. Fields are in ARCs. Holds authentication info, http client(reqwest), and url infos
pub struct ApiClient {
    authent: Arc<Authent>,
    client: Client,
    esi_url: Arc<String>,
    auth_url: Arc<String>,
}

impl Clone for ApiClient {
    fn clone(&self) -> Self {
        Self {
            authent: self.authent.clone(),
            client: self.client.clone(),
            esi_url: self.esi_url.clone(),
            auth_url: self.auth_url.clone(),
        }
    }
}

impl ApiClient {
    pub(crate) fn new(
        authent: Authent,
        esi_url: String,
        auth_url: String,
        user_agent: String,
    ) -> Result<Self> {
        let client = reqwest::Client::builder().user_agent(user_agent).build()?;

        Ok(ApiClient {
            authent: Arc::new(authent),
            client,
            esi_url: Arc::new(esi_url),
            auth_url: Arc::new(auth_url),
        })
    }

    /// Api GET call on path. Response is deserialized in T with serde crate.
    pub async fn query_esi<T>(&self, path: String) -> Result<Option<T>>
    where
        T: DeserializeOwned,
    {
        let response = self.raw_query(&self.esi_url, path).await?;
        if response.status() == StatusCode::NOT_FOUND {
            Ok(None)
        } else {
            // todo: handle 404 to None
            let text_response = response.text().await?;
            debug!("received {} chars", text_response.len());
            debug!("response : {:?}", text_response);
            let parsed = serde_json::from_str(text_response.as_str())?;
            Ok(Some(parsed))
        }
    }

    /// Api GET call on path, with url parameters. Response is deserialized in T with serde crate.
    pub async fn query_esi_with_params<T>(
        &self,
        path: String,
        params: HashMap<String, String>,
    ) -> Result<Option<T>>
    where
        T: DeserializeOwned,
    {
        let response = self
            .raw_query_with_params(&self.esi_url, path, params)
            .await?;
        if response.status() == StatusCode::NOT_FOUND {
            Ok(None)
        } else {
            // todo: handle 404 to None
            let text_response = response.text().await?;
            debug!("received {} chars", text_response.len());
            debug!("response : {:?}", text_response);
            let parsed = serde_json::from_str(text_response.as_str())?;
            Ok(Some(parsed))
        }
    }

    pub async fn query_auth<T>(&self, path: String) -> Result<T>
    where
        T: DeserializeOwned,
    {
        let text_response = self.raw_query(&self.auth_url, path).await?.text().await?;

        let parsed = serde_json::from_str(text_response.as_str())?;
        Ok(parsed)
    }

    async fn raw_query(&self, url: &String, path: String) -> Result<Response> {
        let r = self
            .client
            .get(format!("{}/{}", url, path))
            .bearer_auth(self.authent.token.access_token().secret())
            .send()
            .await?;
        debug!("query: {} ", path);
        match r.status() {
            StatusCode::OK => Ok(r),
            status => Err(EveEsiError::ApiCallError(status, r.text().await?)),
        }
    }

    async fn raw_query_with_params(
        &self,
        base: &String,
        path: String,
        params: HashMap<String, String>,
    ) -> Result<Response> {
        let url = format!("{}/{}", base, path);
        let url_params = reqwest::Url::parse_with_params(&url, params.into_iter())?;
        debug!("URL to call : '{}'", url_params);
        let r = self
            .client
            .get(url_params)
            .bearer_auth(self.authent.token.access_token().secret())
            .header("Accept", "application/json")
            .header("Cache-Control", "no-cache")
            .send()
            .await?;
        debug!("query: {} ", path);
        match r.status() {
            StatusCode::OK => Ok(r),
            status => Err(EveEsiError::ApiCallError(status, r.text().await?)),
        }
    }
}