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}");
}
}