1use std::{net::SocketAddr, sync::Arc};
2
3use crate::{
4 ApiAuthenticator, ApiError, ApiResult, AuthenticateMiddleware, Client, ClientBuilder,
5 DnsResolver, Initialiser, IntoUrl, LogConfig, LogMiddleware, Method, Middleware,
6 RequestBuilder, RequestTraceIdMiddleware, ReqwestDnsResolver, ReqwestUrlRewriter, Url, UrlOps,
7 UrlRewriter,
8};
9
10pub struct ApiBuilder {
12 client: ClientBuilder,
14 base_url: Url,
16 rewriter: Option<ReqwestUrlRewriter>,
18 resolver: Option<ReqwestDnsResolver>,
20 authenticator: Option<Arc<dyn ApiAuthenticator>>,
22 logger: Option<Arc<LogConfig>>,
24 initialisers: Vec<Arc<dyn Initialiser>>,
26 middlewares: Vec<Arc<dyn Middleware>>,
28}
29
30impl ApiBuilder {
31 pub fn new(base_url: impl IntoUrl + std::fmt::Debug) -> ApiResult<Self> {
34 Ok(Self {
35 client: ClientBuilder::default(),
36 base_url: base_url.into_url().map_err(ApiError::InvalidUrl)?,
37 rewriter: None,
38 resolver: None,
39 authenticator: None,
40 logger: None,
41 initialisers: vec![],
42 middlewares: vec![],
43 })
44 }
45
46 pub fn with_client(self, client: ClientBuilder) -> Self {
49 Self { client, ..self }
50 }
51
52 pub fn with_rewriter<T>(self, rewriter: T) -> Self
55 where
56 T: UrlRewriter,
57 {
58 Self {
59 rewriter: Some(ReqwestUrlRewriter::new(rewriter)),
60 ..self
61 }
62 }
63
64 pub fn with_resolver<T>(self, resolver: T) -> Self
67 where
68 T: DnsResolver,
69 {
70 Self {
71 resolver: Some(ReqwestDnsResolver::new(resolver)),
72 ..self
73 }
74 }
75
76 pub fn with_authenticator<T>(self, authenticator: T) -> Self
79 where
80 T: ApiAuthenticator,
81 {
82 Self {
83 authenticator: Some(Arc::new(authenticator)),
84 ..self
85 }
86 }
87
88 pub fn with_logger<T>(self, logger: T) -> Self
91 where
92 T: Into<LogConfig>,
93 {
94 Self {
95 logger: Some(Arc::new(logger.into())),
96 ..self
97 }
98 }
99
100 pub fn with_initialiser<T>(self, initialiser: T) -> Self
103 where
104 T: Initialiser,
105 {
106 let mut s = self;
107 s.initialisers.push(Arc::new(initialiser));
108 s
109 }
110
111 pub fn with_middleware<T>(self, middleware: T) -> Self
114 where
115 T: Middleware,
116 {
117 let mut s = self;
118 s.middlewares.push(Arc::new(middleware));
119 s
120 }
121
122 pub fn build(self) -> ApiCore {
124 let client = match self.resolver.clone() {
125 Some(r) => self.client.dns_resolver(Arc::new(r)),
126 None => self.client,
127 };
128 let mut client = reqwest_middleware::ClientBuilder::new(client.build().unwrap());
129
130 client = client.with(RequestTraceIdMiddleware);
132 for middleware in self.middlewares {
134 client = client.with_arc(middleware);
135 }
136 if self.authenticator.is_some() {
137 client = client.with(AuthenticateMiddleware);
138 }
139 client = client.with(LogMiddleware);
140
141 if let Some(logger) = self.logger {
143 client = client.with_arc_init(logger);
144 }
145 for initialiser in self.initialisers {
146 client = client.with_arc_init(initialiser);
147 }
148
149 ApiCore {
150 client: client.build(),
151 base_url: self.base_url,
152 rewriter: self.rewriter,
153 resolver: self.resolver,
154 authenticator: self.authenticator,
155 }
156 }
157}
158
159pub struct ApiCore {
161 client: Client,
163 base_url: Url,
165 rewriter: Option<ReqwestUrlRewriter>,
167 resolver: Option<ReqwestDnsResolver>,
169 authenticator: Option<Arc<dyn ApiAuthenticator>>,
171}
172
173impl std::fmt::Debug for ApiCore {
174 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
175 let mut d = f.debug_struct("ApiCore");
176 let mut d = d
177 .field("client", &self.client)
178 .field("base_url", &self.base_url);
179 if let Some(r) = self.rewriter.as_ref() {
180 d = d.field("rewriter", &r.type_name());
181 }
182 if let Some(r) = self.resolver.as_ref() {
183 d = d.field("resolver", &r.type_name());
184 }
185 if let Some(s) = self.authenticator.as_ref() {
186 d = d.field("authenticator", &s.type_name());
187 }
188 d.finish()
189 }
190}
191
192impl ApiCore {
193 pub fn rebase(&self, base_url: impl IntoUrl) -> ApiResult<Self> {
195 let base_url = base_url.into_url().map_err(ApiError::InvalidUrl)?;
196 Ok(Self {
197 client: self.client.clone(),
198 base_url,
199 rewriter: self.rewriter.clone(),
200 resolver: self.resolver.clone(),
201 authenticator: self.authenticator.clone(),
202 })
203 }
204
205 pub fn with_rewriter<T>(&self, rewriter: T) -> Self
208 where
209 T: UrlRewriter,
210 {
211 Self {
212 client: self.client.clone(),
213 base_url: self.base_url.clone(),
214 rewriter: Some(ReqwestUrlRewriter::new(rewriter)),
215 resolver: self.resolver.clone(),
216 authenticator: self.authenticator.clone(),
217 }
218 }
219
220 pub fn with_resolver<T>(&self, resolver: T) -> Self
223 where
224 T: DnsResolver,
225 {
226 Self {
227 client: self.client.clone(),
228 base_url: self.base_url.clone(),
229 rewriter: self.rewriter.clone(),
230 resolver: Some(ReqwestDnsResolver::new(resolver)),
231 authenticator: self.authenticator.clone(),
232 }
233 }
234
235 pub fn with_endpoint<T>(&self, endpoint: T) -> Self
238 where
239 T: Into<SocketAddr>,
240 {
241 self.with_rewriter(endpoint.into())
242 }
243
244 pub fn with_authenticator<T>(&self, authenticator: T) -> Self
247 where
248 T: ApiAuthenticator,
249 {
250 Self {
251 client: self.client.clone(),
252 base_url: self.base_url.clone(),
253 rewriter: self.rewriter.clone(),
254 resolver: self.resolver.clone(),
255 authenticator: Some(Arc::new(authenticator)),
256 }
257 }
258
259 async fn build_base_url(&self) -> Result<Url, ApiError> {
261 let mut base_url = self.base_url.clone();
262 if let Some(router) = self.rewriter.as_ref() {
263 base_url = router.rewrite(base_url).await?;
264 }
265 if let Some(resolver) = self.resolver.as_ref() {
266 base_url = resolver.rewrite(base_url).await?;
267 }
268 Ok(base_url)
269 }
270
271 pub async fn build_url(&self, path: impl AsRef<str>) -> ApiResult<Url> {
276 let base = self.build_base_url().await?;
277 Ok(base.merge_path(path.as_ref()))
278 }
279
280 pub async fn build_request(
284 &self,
285 method: Method,
286 path: impl AsRef<str>,
287 ) -> ApiResult<RequestBuilder> {
288 let url = self.build_url(path.as_ref()).await?;
289 let req = self.client.request(method, url);
290
291 match self.authenticator.clone() {
292 Some(authenticator) => Ok(req.with_extension(authenticator)),
293 None => Ok(req),
294 }
295 }
296}