use std::borrow::Cow;
use bytes::Bytes;
use reqwest_middleware::RequestBuilder;
use serde::Serialize;
use serde::de::DeserializeOwned;
use crate::client::multipart::Multipart;
use crate::client::{Client, EndpointReply, Headers};
use crate::error::Result;
#[derive(Debug, Clone)]
pub(crate) struct EndpointBase {
client: Client,
route: Cow<'static, str>,
headers: Headers,
}
impl EndpointBase {
pub(crate) fn new(client: Client, route: Cow<'static, str>) -> Self {
Self {
client,
route,
headers: Headers::default(),
}
}
pub(crate) fn route(&self) -> &str {
&self.route
}
#[cfg(feature = "tracing")]
pub(crate) fn request_id(&self) -> Option<&str> {
self.headers.request_id()
}
pub(crate) fn insert_header(&mut self, name: impl AsRef<str>, value: impl AsRef<str>) {
self.headers.insert(name, value);
}
pub(crate) fn client(&self) -> &Client {
&self.client
}
pub(crate) fn route_cow(&self) -> Cow<'static, str> {
self.route.clone()
}
pub(crate) fn headers(&self) -> &Headers {
&self.headers
}
pub(crate) fn request(&self, route: &str) -> Result<RequestBuilder> {
self.headers.apply(self.client.post(route)?)
}
}
#[derive(Debug, Clone)]
pub struct Endpoint {
base: EndpointBase,
}
impl Endpoint {
pub(crate) fn new(client: Client, route: Cow<'static, str>) -> Self {
Self {
base: EndpointBase::new(client, route),
}
}
pub fn route(&self) -> &str {
self.base.route()
}
pub fn with_header(mut self, name: impl AsRef<str>, value: impl AsRef<str>) -> Self {
self.base.insert_header(name, value);
self
}
pub fn with_headers<N, V, I>(mut self, headers: I) -> Self
where
N: AsRef<str>,
V: AsRef<str>,
I: IntoIterator<Item = (N, V)>,
{
for (name, value) in headers {
self.base.insert_header(name, value);
}
self
}
pub fn with_request_id(self, id: impl AsRef<str>) -> Self {
self.with_header("x-request-id", id)
}
#[cfg_attr(feature = "tracing", tracing::instrument(skip(self, payload), fields(route = %self.base.route(), request_id = self.base.request_id()), err))]
pub async fn call<T>(&self, payload: &T) -> Result<EndpointReply>
where
T: Serialize + ?Sized,
{
let req = self.base.request(self.base.route())?.json(payload);
Ok(EndpointReply::new(self.base.client().send(req).await?))
}
pub async fn invoke<T, R>(&self, payload: &T) -> Result<R>
where
T: Serialize + ?Sized,
R: DeserializeOwned,
{
self.call(payload).await?.json().await
}
#[cfg_attr(feature = "tracing", tracing::instrument(skip(self, body), fields(route = %self.base.route(), request_id = self.base.request_id()), err))]
pub async fn call_bytes(&self, body: impl Into<Bytes>) -> Result<EndpointReply> {
let req = self.base.request(self.base.route())?.body(body.into());
Ok(EndpointReply::new(self.base.client().send(req).await?))
}
#[cfg_attr(feature = "tracing", tracing::instrument(skip(self, body), fields(route = %self.base.route(), request_id = self.base.request_id()), err))]
pub async fn call_multipart(&self, body: Multipart) -> Result<EndpointReply> {
let req = self
.base
.request(self.base.route())?
.multipart(body.into_form()?);
Ok(EndpointReply::new(self.base.client().send(req).await?))
}
}