use std::marker::PhantomData;
use async_trait::async_trait;
use http::{Request, Response};
use serde::de::DeserializeOwned;
#[cfg(feature = "blocking")]
use crate::blocking::client::Client as BlockingClient;
use crate::{
client::Client,
enums::{RequestMethod, RequestType, ResponseType},
errors::ClientError,
};
pub trait Wrapper: DeserializeOwned + Send + Sync {
type Value;
}
pub struct MutatedEndpoint<'a, E: Endpoint, M: MiddleWare> {
endpoint: E,
middleware: &'a M,
}
impl<'a, E: Endpoint, M: MiddleWare> MutatedEndpoint<'a, E, M> {
pub fn new(endpoint: E, middleware: &'a M) -> Self {
MutatedEndpoint {
endpoint,
middleware,
}
}
}
#[async_trait]
impl<E: Endpoint, M: MiddleWare> Endpoint for MutatedEndpoint<'_, E, M> {
type Response = E::Response;
const REQUEST_BODY_TYPE: RequestType = E::REQUEST_BODY_TYPE;
const RESPONSE_BODY_TYPE: ResponseType = E::RESPONSE_BODY_TYPE;
fn path(&self) -> String {
self.endpoint.path()
}
fn method(&self) -> RequestMethod {
self.endpoint.method()
}
fn query(&self) -> Result<Option<String>, ClientError> {
self.endpoint.query()
}
fn body(&self) -> Result<Option<Vec<u8>>, ClientError> {
self.endpoint.body()
}
#[instrument(skip(self), err)]
fn url(&self, base: &str) -> Result<http::Uri, ClientError> {
self.endpoint.url(base)
}
#[instrument(skip(self), err)]
fn request(&self, base: &str) -> Result<Request<Vec<u8>>, ClientError> {
let mut req = crate::http::build_request(
base,
&self.path(),
self.method(),
self.query()?,
self.body()?,
Self::REQUEST_BODY_TYPE,
)?;
self.middleware.request(self, &mut req)?;
Ok(req)
}
#[instrument(skip(self, client), err)]
async fn exec(
&self,
client: &impl Client,
) -> Result<EndpointResult<Self::Response>, ClientError> {
debug!("Executing endpoint");
let req = self.request(client.base())?;
let resp = exec_mut(client, self, req, self.middleware).await?;
Ok(EndpointResult::new(resp, Self::RESPONSE_BODY_TYPE))
}
#[cfg(feature = "blocking")]
fn exec_block(
&self,
client: &impl BlockingClient,
) -> Result<EndpointResult<Self::Response>, ClientError> {
debug!("Executing endpoint");
let req = self.request(client.base())?;
let resp = exec_block_mut(client, self, req, self.middleware)?;
Ok(EndpointResult::new(resp, Self::RESPONSE_BODY_TYPE))
}
}
#[async_trait]
pub trait Endpoint: Send + Sync + Sized {
type Response: DeserializeOwned + Send + Sync;
const REQUEST_BODY_TYPE: RequestType;
const RESPONSE_BODY_TYPE: ResponseType;
fn path(&self) -> String;
fn method(&self) -> RequestMethod;
fn query(&self) -> Result<Option<String>, ClientError> {
Ok(None)
}
fn body(&self) -> Result<Option<Vec<u8>>, ClientError> {
Ok(None)
}
#[instrument(skip(self), err)]
fn url(&self, base: &str) -> Result<http::Uri, ClientError> {
crate::http::build_url(base, &self.path(), self.query()?)
}
#[instrument(skip(self), err)]
fn request(&self, base: &str) -> Result<Request<Vec<u8>>, ClientError> {
crate::http::build_request(
base,
&self.path(),
self.method(),
self.query()?,
self.body()?,
Self::REQUEST_BODY_TYPE,
)
}
#[instrument(skip(self, client), err)]
async fn exec(
&self,
client: &impl Client,
) -> Result<EndpointResult<Self::Response>, ClientError> {
debug!("Executing endpoint");
let req = self.request(client.base())?;
let resp = exec(client, req).await?;
Ok(EndpointResult::new(resp, Self::RESPONSE_BODY_TYPE))
}
fn with_middleware<M: MiddleWare>(self, middleware: &M) -> MutatedEndpoint<Self, M> {
MutatedEndpoint::new(self, middleware)
}
#[cfg(feature = "blocking")]
#[instrument(skip(self, client), err)]
fn exec_block(
&self,
client: &impl BlockingClient,
) -> Result<EndpointResult<Self::Response>, ClientError> {
debug!("Executing endpoint");
let req = self.request(client.base())?;
let resp = exec_block(client, req)?;
Ok(EndpointResult::new(resp, Self::RESPONSE_BODY_TYPE))
}
}
pub struct EndpointResult<T: DeserializeOwned + Send + Sync> {
pub response: Response<Vec<u8>>,
pub ty: ResponseType,
inner: PhantomData<T>,
}
impl<T: DeserializeOwned + Send + Sync> EndpointResult<T> {
pub fn new(response: Response<Vec<u8>>, ty: ResponseType) -> Self {
EndpointResult {
response,
ty,
inner: PhantomData,
}
}
#[instrument(skip(self), err)]
pub fn parse(&self) -> Result<T, ClientError> {
match self.ty {
ResponseType::JSON => serde_json::from_slice(self.response.body()).map_err(|e| {
ClientError::ResponseParseError {
source: e.into(),
content: String::from_utf8(self.response.body().to_vec()).ok(),
}
}),
}
}
pub fn raw(&self) -> Vec<u8> {
self.response.body().clone()
}
#[instrument(skip(self), err)]
pub fn wrap<W>(&self) -> Result<W, ClientError>
where
W: Wrapper<Value = T>,
{
match self.ty {
ResponseType::JSON => serde_json::from_slice(self.response.body()).map_err(|e| {
ClientError::ResponseParseError {
source: e.into(),
content: String::from_utf8(self.response.body().to_vec()).ok(),
}
}),
}
}
}
pub trait MiddleWare: Sync + Send {
fn request<E: Endpoint>(
&self,
endpoint: &E,
req: &mut Request<Vec<u8>>,
) -> Result<(), ClientError>;
fn response<E: Endpoint>(
&self,
endpoint: &E,
resp: &mut Response<Vec<u8>>,
) -> Result<(), ClientError>;
}
async fn exec(
client: &impl Client,
req: Request<Vec<u8>>,
) -> Result<Response<Vec<u8>>, ClientError> {
client.execute(req).await
}
async fn exec_mut(
client: &impl Client,
endpoint: &impl Endpoint,
req: Request<Vec<u8>>,
middle: &impl MiddleWare,
) -> Result<Response<Vec<u8>>, ClientError> {
let mut resp = client.execute(req).await?;
middle.response(endpoint, &mut resp)?;
Ok(resp)
}
#[cfg(feature = "blocking")]
fn exec_block(
client: &impl BlockingClient,
req: Request<Vec<u8>>,
) -> Result<Response<Vec<u8>>, ClientError> {
client.execute(req)
}
#[cfg(feature = "blocking")]
fn exec_block_mut(
client: &impl BlockingClient,
endpoint: &impl Endpoint,
req: Request<Vec<u8>>,
middle: &impl MiddleWare,
) -> Result<Response<Vec<u8>>, ClientError> {
let mut resp = client.execute(req)?;
middle.response(endpoint, &mut resp)?;
Ok(resp)
}