use std::{net::SocketAddr, sync::Arc};
use crate::{
ApiAuthenticator, ApiError, ApiResult, AuthenticateMiddleware, Client, ClientBuilder,
DnsResolver, Initialiser, IntoUrl, LogConfig, LogMiddleware, Method, Middleware,
RequestBuilder, RequestTraceIdMiddleware, ReqwestDnsResolver, ReqwestUrlRewriter, Url, UrlOps,
UrlRewriter,
};
pub struct ApiBuilder {
client: ClientBuilder,
base_url: Url,
rewriter: Option<ReqwestUrlRewriter>,
resolver: Option<ReqwestDnsResolver>,
authenticator: Option<Arc<dyn ApiAuthenticator>>,
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)?,
rewriter: None,
resolver: None,
authenticator: None,
logger: None,
initialisers: vec![],
middlewares: vec![],
})
}
pub fn with_client(self, client: ClientBuilder) -> Self {
Self { client, ..self }
}
pub fn with_rewriter<T>(self, rewriter: T) -> Self
where
T: UrlRewriter,
{
Self {
rewriter: Some(ReqwestUrlRewriter::new(rewriter)),
..self
}
}
pub fn with_resolver<T>(self, resolver: T) -> Self
where
T: DnsResolver,
{
Self {
resolver: Some(ReqwestDnsResolver::new(resolver)),
..self
}
}
pub fn with_authenticator<T>(self, authenticator: T) -> Self
where
T: ApiAuthenticator,
{
Self {
authenticator: Some(Arc::new(authenticator)),
..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 client = match self.resolver.clone() {
Some(r) => self.client.dns_resolver(Arc::new(r)),
None => self.client,
};
let mut client = reqwest_middleware::ClientBuilder::new(client.build().unwrap());
client = client.with(RequestTraceIdMiddleware);
for middleware in self.middlewares {
client = client.with_arc(middleware);
}
if self.authenticator.is_some() {
client = client.with(AuthenticateMiddleware);
}
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,
rewriter: self.rewriter,
resolver: self.resolver,
authenticator: self.authenticator,
}
}
}
pub struct ApiCore {
client: Client,
base_url: Url,
rewriter: Option<ReqwestUrlRewriter>,
resolver: Option<ReqwestDnsResolver>,
authenticator: Option<Arc<dyn ApiAuthenticator>>,
}
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.rewriter.as_ref() {
d = d.field("rewriter", &r.type_name());
}
if let Some(r) = self.resolver.as_ref() {
d = d.field("resolver", &r.type_name());
}
if let Some(s) = self.authenticator.as_ref() {
d = d.field("authenticator", &s.type_name());
}
d.finish()
}
}
impl ApiCore {
pub fn rebase(&self, base_url: impl IntoUrl) -> ApiResult<Self> {
let base_url = base_url.into_url().map_err(ApiError::InvalidUrl)?;
Ok(Self {
client: self.client.clone(),
base_url,
rewriter: self.rewriter.clone(),
resolver: self.resolver.clone(),
authenticator: self.authenticator.clone(),
})
}
pub fn with_rewriter<T>(&self, rewriter: T) -> Self
where
T: UrlRewriter,
{
Self {
client: self.client.clone(),
base_url: self.base_url.clone(),
rewriter: Some(ReqwestUrlRewriter::new(rewriter)),
resolver: self.resolver.clone(),
authenticator: self.authenticator.clone(),
}
}
pub fn with_resolver<T>(&self, resolver: T) -> Self
where
T: DnsResolver,
{
Self {
client: self.client.clone(),
base_url: self.base_url.clone(),
rewriter: self.rewriter.clone(),
resolver: Some(ReqwestDnsResolver::new(resolver)),
authenticator: self.authenticator.clone(),
}
}
pub fn with_endpoint<T>(&self, endpoint: T) -> Self
where
T: Into<SocketAddr>,
{
self.with_rewriter(endpoint.into())
}
async fn build_base_url(&self) -> Result<Url, ApiError> {
let mut base_url = self.base_url.clone();
if let Some(router) = self.rewriter.as_ref() {
base_url = router.rewrite(base_url).await?;
}
if let Some(resolver) = self.resolver.as_ref() {
base_url = resolver.rewrite(base_url).await?;
}
Ok(base_url)
}
pub async fn build_url(&self, path: impl AsRef<str>) -> ApiResult<Url> {
let base = self.build_base_url().await?;
Ok(base.merge_path(path.as_ref()))
}
pub async fn build_request(
&self,
method: Method,
path: impl AsRef<str>,
) -> ApiResult<RequestBuilder> {
let url = self.build_url(path.as_ref()).await?;
let req = self.client.request(method, url);
match self.authenticator.clone() {
Some(authenticator) => Ok(req.with_extension(authenticator)),
None => Ok(req),
}
}
}