xapi-shared 0.0.1

XAPI Shared Library
Documentation
use crate::{
    ratelimiter::SharedRatelimiterTrait,
    rest::{
        error::{SharedRestClientErrorTrait, SharedRestError},
        payload::SharedRestPayloadTrait,
        response::SharedRestResponseTrait,
    },
    signer::SharedSignerTrait,
};
use http::Method;
use reqwest::Url;
use std::{num::NonZeroU32, sync::Arc};

pub mod error;
pub mod payload;
pub mod response;

#[async_trait::async_trait]
pub trait SharedRestClientTrait<RateLimitType: Sync> {
    fn get_client(&self) -> &reqwest::Client;
    fn get_signer(&self) -> &dyn SharedSignerTrait;
    fn get_ratelimiter(&self) -> Arc<dyn SharedRatelimiterTrait<RateLimitType> + Sync + Send>;

    async fn call_with_no_payload<
        ResponseType: SharedRestResponseTrait,
        Err: SharedRestClientErrorTrait,
    >(
        &self,
        limits: &[(RateLimitType, NonZeroU32)],
        signed: bool,
        method: Method,
        url: Url,
    ) -> Result<ResponseType, SharedRestError<Err>> {
        self.call::<(), ResponseType, Err>(limits, signed, method, url, None)
            .await
    }

    async fn call_with_no_response<
        PayloadType: Sync + Send + SharedRestPayloadTrait,
        Err: SharedRestClientErrorTrait,
    >(
        &self,
        limits: &[(RateLimitType, NonZeroU32)],
        signed: bool,
        method: Method,
        url: Url,
        payload: Option<&PayloadType>,
    ) -> Result<(), SharedRestError<Err>> {
        self.call::<PayloadType, (), Err>(limits, signed, method, url, payload)
            .await
    }

    async fn call_with_no_payload_and_response<Err: SharedRestClientErrorTrait>(
        &self,
        limits: &[(RateLimitType, NonZeroU32)],
        signed: bool,
        method: Method,
        url: Url,
    ) -> Result<(), SharedRestError<Err>> {
        self.call::<(), (), Err>(limits, signed, method, url, None)
            .await
    }

    async fn call<
        PayloadType: Sync + Send + SharedRestPayloadTrait,
        ResponseType: SharedRestResponseTrait,
        Err: SharedRestClientErrorTrait,
    >(
        &self,
        limits: &[(RateLimitType, NonZeroU32)],
        signed: bool,
        method: Method,
        url: Url,
        payload: Option<&PayloadType>,
    ) -> Result<ResponseType, SharedRestError<Err>> {
        for (rate_limit_type, limit) in limits.iter() {
            self.get_ratelimiter()
                .limit_on(rate_limit_type, *limit)
                .await?;
        }

        let mut request = self
            .get_client()
            .request(method, url)
            .build()
            .expect("failed to build request");

        if let Some(payload) = payload {
            payload.build_request(&mut request);
        }

        if signed {
            self.get_signer().sign_request(&mut request);
        }

        let response = self.get_client().execute(request).await?;

        let status = response.status();
        let body = response
            .text()
            .await
            .inspect_err(|err| tracing::error!("failed to get response body: {err}"))?;

        if status.is_success() {
            return ResponseType::from_body(&body);
        }

        if status.is_client_error() {
            tracing::error!("client error: {status} - {body}");
            return Err(SharedRestError::ClientError(Err::from_body(&body)));
        }

        if status.is_server_error() {
            tracing::error!("server error: {status} - {body}");
            return Err(SharedRestError::ServerError(body));
        }

        tracing::error!("uncaught response status: {status} - {body}");
        panic!("uncaught response status: {status} - {body}");
    }
}