apisdk/
core.rs

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
10/// This struct is used to build an instance of ApiCore
11pub struct ApiBuilder {
12    /// Reqwest ClientBuilder
13    client: ClientBuilder,
14    /// Base url for target api
15    base_url: Url,
16    /// The holder of UrlRewriter
17    rewriter: Option<ReqwestUrlRewriter>,
18    /// The holder of DnsResolver
19    resolver: Option<ReqwestDnsResolver>,
20    /// The holder of ApiAuthenticator
21    authenticator: Option<Arc<dyn ApiAuthenticator>>,
22    /// The holder of LogConfig
23    logger: Option<Arc<LogConfig>>,
24    /// The initialisers for Reqwest
25    initialisers: Vec<Arc<dyn Initialiser>>,
26    /// The middlewares for Reqwest
27    middlewares: Vec<Arc<dyn Middleware>>,
28}
29
30impl ApiBuilder {
31    /// Create an instance of ApiBuilder
32    /// - base_url: base url for target api
33    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    /// Set the ClientBuilder to create Client instance of Reqwest
47    /// - client: Reqwest ClientBuilder
48    pub fn with_client(self, client: ClientBuilder) -> Self {
49        Self { client, ..self }
50    }
51
52    /// Set the UrlRewriter
53    /// - resolver: UrlRewriter
54    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    /// Set the DnsResolver
65    /// - resolver: DnsResolver
66    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    /// Set the ApiAuthenticator
77    /// - authenticator: ApiAuthenticator
78    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    /// Set the LogConfig
89    /// - logger: LogConfig
90    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    /// Add initialiser
101    /// - initialiser: Reqwest Initialiser
102    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    /// Add middleware
112    /// - middleware: Reqwest Middleware
113    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    /// Build an instance of ApiCore
123    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        // Apply middleware in correct order
131        client = client.with(RequestTraceIdMiddleware);
132        // client = client.with(RewriteHostMiddleware);
133        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        // Apply initialisers
142        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
159/// This struct is used to create HTTP request
160pub struct ApiCore {
161    /// Reqwest Client
162    client: Client,
163    /// Base url for target api
164    base_url: Url,
165    /// The holder of ReqwestUrlRewriter
166    rewriter: Option<ReqwestUrlRewriter>,
167    /// The holder of ReqwestDnsResolver
168    resolver: Option<ReqwestDnsResolver>,
169    /// The holder of ApiAuthenticator
170    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    /// Create a new ApiCore with a different base_url
194    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    /// Set the UrlRewriter
206    /// - resolver: UrlRewriter
207    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    /// Set the DnsResolver
221    /// - resolver: DnsResolver
222    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    /// Set rewriter to use endpoint
236    /// - endpoint: SocketAddr
237    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    /// Set the Authenticator
245    /// - authenticator: ApiAuthenticator
246    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    /// Build base_url
260    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    /// Build a new request url
272    /// - path: relative path to base_url
273    ///
274    /// Return error when failed to retrieve valid endpoint from ApiRouter
275    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    /// Build a new HTTP request
281    /// - method: HTTP method
282    /// - path: relative path to base_url
283    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}