use std::sync::Arc;
use reqwest::header::HOST;
use crate::{
ApiResult, ApiRouter, ApiSignature, Client, ClientBuilder, Initialiser, IntoUrl, Method,
Middleware, OriginalEndpoint, RequestBuilder, RequestTraceIdInjector, SignatureMiddleware, Url,
};
pub struct ApiBuilder {
client: ClientBuilder,
base_url: Url,
router: Option<Arc<dyn ApiRouter>>,
signature: Option<Arc<dyn ApiSignature>>,
initialisers: Vec<Arc<dyn Initialiser>>,
middlewares: Vec<Arc<dyn Middleware>>,
}
impl ApiBuilder {
pub fn new(base_url: impl IntoUrl + std::fmt::Debug) -> ApiResult<Self> {
let request_trace_id_injector = Arc::new(RequestTraceIdInjector {});
Ok(Self {
client: ClientBuilder::default(),
base_url: base_url.into_url()?,
router: None,
signature: None,
initialisers: vec![],
middlewares: vec![request_trace_id_injector],
})
}
pub fn with_client(self, client: ClientBuilder) -> Self {
Self { client, ..self }
}
pub fn with_router(self, router: impl ApiRouter) -> Self {
Self {
router: Some(Arc::new(router)),
..self
}
}
pub fn with_signature(self, signature: impl ApiSignature) -> Self {
Self {
signature: Some(Arc::new(signature)),
..self
}
}
pub fn with_initialiser(self, initialiser: impl Initialiser) -> Self {
let mut s = self;
s.initialisers.push(Arc::new(initialiser));
s
}
pub fn with_middleware(self, middleware: impl Middleware) -> Self {
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());
for initialiser in self.initialisers {
client = client.with_arc_init(initialiser);
}
for middleware in self.middlewares {
client = client.with_arc(middleware);
}
if self.signature.is_some() {
client = client.with(SignatureMiddleware);
}
ApiCore {
client: client.build(),
base_url: self.base_url,
router: self.router,
signature: self.signature,
}
}
}
#[derive(Debug)]
pub struct ApiCore {
client: Client,
base_url: Url,
router: Option<Arc<dyn ApiRouter>>,
signature: Option<Arc<dyn ApiSignature>>,
}
impl ApiCore {
pub async fn build_url(&self, path: impl AsRef<str>) -> ApiResult<Url> {
let endpoint = match self.router.as_ref() {
Some(router) => router.next_endpoint().await?,
None => Box::new(OriginalEndpoint {}),
};
endpoint
.build_url(&self.base_url, path.as_ref())
.map_err(|e| e.into())
}
pub async fn build_request(
&self,
method: Method,
path: impl AsRef<str>,
) -> ApiResult<RequestBuilder> {
let url = self.build_url(path).await?;
let mut req = self.client.request(method, url);
if !self
.router
.as_ref()
.map(|r| r.rewrite_host())
.unwrap_or_default()
{
if let Some(host) = self.base_url.host_str() {
req = req.header(HOST, host);
}
}
match self.signature.clone() {
Some(signature) => Ok(req.with_extension(signature)),
None => Ok(req),
}
}
}