use std::sync::Arc;
use crate::{
ApiError, ApiResult, ApiRouter, ApiSignature, Client, ClientBuilder, Initialiser, IntoUrl,
LogConfig, LogMiddleware, Method, Middleware, OriginalEndpoint, RequestBuilder,
RequestTraceIdMiddleware, RewriteHost, RewriteHostMiddleware, SignatureMiddleware, Url,
};
pub struct ApiBuilder {
client: ClientBuilder,
base_url: Url,
router: Option<Arc<dyn ApiRouter>>,
signature: Option<Arc<dyn ApiSignature>>,
logger: Option<Arc<LogConfig>>,
initialisers: Vec<Arc<dyn Initialiser>>,
middlewares: Vec<Arc<dyn Middleware>>,
}
impl ApiBuilder {
pub fn new(base_url: impl IntoUrl + std::fmt::Debug) -> ApiResult<Self> {
Ok(Self {
client: ClientBuilder::default(),
base_url: base_url.into_url().map_err(ApiError::InvalidUrl)?,
router: None,
signature: None,
logger: None,
initialisers: vec![],
middlewares: vec![],
})
}
pub fn with_client(self, client: ClientBuilder) -> Self {
Self { client, ..self }
}
pub fn with_router<T>(self, router: T) -> Self
where
T: ApiRouter,
{
Self {
router: Some(Arc::new(router)),
..self
}
}
pub fn with_signature<T>(self, signature: T) -> Self
where
T: ApiSignature,
{
Self {
signature: Some(Arc::new(signature)),
..self
}
}
pub fn with_logger<T>(self, logger: T) -> Self
where
T: Into<LogConfig>,
{
Self {
logger: Some(Arc::new(logger.into())),
..self
}
}
pub fn with_initialiser<T>(self, initialiser: T) -> Self
where
T: Initialiser,
{
let mut s = self;
s.initialisers.push(Arc::new(initialiser));
s
}
pub fn with_middleware<T>(self, middleware: T) -> Self
where
T: Middleware,
{
let mut s = self;
s.middlewares.push(Arc::new(middleware));
s
}
pub fn build(self) -> ApiCore {
let mut client = reqwest_middleware::ClientBuilder::new(self.client.build().unwrap());
client = client.with(RequestTraceIdMiddleware);
client = client.with(RewriteHostMiddleware);
for middleware in self.middlewares {
client = client.with_arc(middleware);
}
if self.signature.is_some() {
client = client.with(SignatureMiddleware);
}
if self.logger.is_some() {
client = client.with(LogMiddleware);
}
if let Some(logger) = self.logger {
client = client.with_arc_init(logger);
};
for initialiser in self.initialisers {
client = client.with_arc_init(initialiser);
}
ApiCore {
client: client.build(),
base_url: self.base_url,
router: self.router,
signature: self.signature,
}
}
}
pub struct ApiCore {
client: Client,
base_url: Url,
router: Option<Arc<dyn ApiRouter>>,
signature: Option<Arc<dyn ApiSignature>>,
}
impl std::fmt::Debug for ApiCore {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut d = f.debug_struct("ApiCore");
let mut d = d
.field("client", &self.client)
.field("base_url", &self.base_url);
if let Some(r) = self.router.as_ref() {
d = d.field("router", &r.type_name());
}
if let Some(s) = self.signature.as_ref() {
d = d.field("signature", &s.type_name());
}
d.finish()
}
}
impl ApiCore {
pub fn rebase(&self, base_url: impl IntoUrl) -> ApiResult<Self> {
Ok(Self {
client: self.client.clone(),
base_url: base_url.into_url().map_err(ApiError::InvalidUrl)?,
router: self.router.clone(),
signature: self.signature.clone(),
})
}
pub async fn build_url(&self, path: impl AsRef<str>) -> ApiResult<(Url, bool)> {
let endpoint = match self.router.as_ref() {
Some(router) => router.next_endpoint().await?,
None => Box::new(OriginalEndpoint::default()),
};
endpoint
.build_url(&self.base_url, path.as_ref())
.map(|url| (url, endpoint.reserve_original_host()))
.map_err(|e| e.into())
}
pub async fn build_request(
&self,
method: Method,
path: impl AsRef<str>,
) -> ApiResult<RequestBuilder> {
let (url, reserve_original_host) = self.build_url(path).await?;
let mut req = self.client.request(method, url);
if reserve_original_host {
if let Some(host) = self.base_url.host_str() {
req = req.with_extension(RewriteHost::new(host));
}
}
match self.signature.clone() {
Some(signature) => Ok(req.with_extension(signature)),
None => Ok(req),
}
}
}