use crate::{
client::{Client, Request, Response},
enums::{RequestMethod, RequestType, ResponseType},
errors::ClientError,
};
use serde::{de::DeserializeOwned, Serialize};
use serde_json::Value;
use std::fmt::Debug;
use url::Url;
pub trait Endpoint: Debug + Serialize + Sized {
type Result: DeserializeOwned;
const REQUEST_BODY_TYPE: RequestType;
const RESPONSE_BODY_TYPE: ResponseType;
fn path(&self) -> String;
fn method(&self) -> RequestMethod;
fn query(&self) -> Vec<(String, Value)> {
Vec::new()
}
fn data(&self) -> Option<&[u8]> {
None
}
fn exec<C: Client>(&self, client: &C) -> Result<Option<Self::Result>, ClientError> {
log::info!("Executing endpoint");
log::debug! {"Endpoint: {:#?}", self};
let req = build_request(self, client.base(), self.data())?;
let resp = client.execute(req)?;
parse(self, &resp.body)
}
fn exec_mut<C: Client, M: MiddleWare>(
&self,
client: &C,
middle: &M,
) -> Result<Option<Self::Result>, ClientError> {
log::info!("Executing endpoint");
log::debug! {"Endpoint: {:#?}", self};
let mut req = build_request(self, client.base(), self.data())?;
middle.request(self, &mut req)?;
let mut resp = client.execute(req)?;
middle.response(self, &mut resp)?;
parse(self, &resp.body)
}
fn exec_raw<C: Client>(&self, client: &C) -> Result<Vec<u8>, ClientError> {
log::info!("Executing endpoint");
log::debug! {"Endpoint: {:#?}", self};
let req = build_request(self, client.base(), self.data())?;
let resp = client.execute(req)?;
Ok(resp.body)
}
fn exec_raw_mut<C: Client, M: MiddleWare>(
&self,
client: &C,
middle: &M,
) -> Result<Vec<u8>, ClientError> {
log::info!("Executing endpoint");
log::debug! {"Endpoint: {:#?}", self};
let mut req = build_request(self, client.base(), self.data())?;
middle.request(self, &mut req)?;
let mut resp = client.execute(req)?;
middle.response(self, &mut resp)?;
Ok(resp.body)
}
}
pub trait MiddleWare {
fn request<E: Endpoint>(&self, endpoint: &E, req: &mut Request) -> Result<(), ClientError>;
fn response<E: Endpoint>(&self, endpoint: &E, resp: &mut Response) -> Result<(), ClientError>;
}
fn build_request<E: Endpoint>(
endpoint: &E,
base: &str,
data: Option<&[u8]>,
) -> Result<Request, ClientError> {
let url = build_url(endpoint, base)?;
let method = endpoint.method();
let query = endpoint.query();
let headers = Vec::new();
let body = match data {
Some(d) => d.to_vec(),
None => match E::REQUEST_BODY_TYPE {
RequestType::JSON => {
let parse_data =
serde_json::to_string(endpoint).map_err(|e| ClientError::DataParseError {
source: Box::new(e),
})?;
match parse_data.as_str() {
"null" => "".to_string(),
"{}" => "".to_string(),
_ => parse_data,
}
.into_bytes()
}
},
};
Ok(Request {
url,
method,
query,
headers,
body,
})
}
fn build_url<E: Endpoint>(endpoint: &E, base: &str) -> Result<url::Url, ClientError> {
log::info!(
"Building endpoint url from {} base URL and {} action",
base,
endpoint.path()
);
let mut url = Url::parse(base).map_err(|e| ClientError::UrlParseError {
url: base.to_string(),
source: e,
})?;
url.path_segments_mut()
.unwrap()
.extend(endpoint.path().split('/'));
Ok(url)
}
fn parse<E: Endpoint>(_: &E, body: &[u8]) -> Result<Option<E::Result>, ClientError> {
if body.is_empty() {
return Ok(None);
}
match E::RESPONSE_BODY_TYPE {
ResponseType::JSON => {
serde_json::from_slice(body).map_err(|e| ClientError::ResponseParseError {
source: Box::new(e),
content: String::from_utf8(body.to_vec()).ok(),
})
}
}
}