api_client_framework/
async_client.rs

1use super::endpoint::Endpoint;
2use crate::Error;
3use reqwest::{header::HeaderMap, Response, StatusCode};
4use reqwest_middleware::{ClientBuilder, Middleware};
5use serde::Deserialize;
6use std::{sync::Arc, time::Duration};
7
8#[derive(Clone)]
9pub struct HttpApiClientConfig {
10    /// The maximum time limit for an API request. If a request takes longer than this, it will be
11    /// cancelled.
12    pub http_timeout: Duration,
13    /// A default set of HTTP headers which will be sent with each API request.
14    pub default_headers: HeaderMap,
15    /// Middlewares that will process each API request before the request is actually sent.
16    pub middlewares: Vec<Arc<dyn Middleware>>,
17}
18
19impl Default for HttpApiClientConfig {
20    fn default() -> Self {
21        Self {
22            http_timeout: Duration::from_secs(30),
23            default_headers: HeaderMap::default(),
24            middlewares: Vec::new(),
25        }
26    }
27}
28
29#[derive(Clone)]
30pub struct HttpApiClient {
31    base_url: url::Url,
32    http_client: reqwest_middleware::ClientWithMiddleware,
33}
34
35impl HttpApiClient {
36    pub fn new(base_url: url::Url, config: HttpApiClientConfig) -> Result<Self, Error> {
37        let reqwest_client = reqwest::Client::builder()
38            .default_headers(config.default_headers)
39            .timeout(config.http_timeout)
40            .build()?;
41        let mut client_builder = ClientBuilder::new(reqwest_client);
42        for middleware in config.middlewares {
43            client_builder = client_builder.with_arc(middleware);
44        }
45        let client = client_builder.build();
46
47        Ok(Self {
48            base_url,
49            http_client: client,
50        })
51    }
52
53    /// Issue an API request of the given type.
54    pub async fn request<EndpointType: Endpoint>(
55        &self,
56        endpoint: &EndpointType,
57    ) -> Result<<EndpointType as Endpoint>::Response, Error> {
58        // Build the request
59        let mut request = self
60            .http_client
61            .request(endpoint.method(), endpoint.url(&self.base_url));
62
63        if let Some(body) = endpoint.body() {
64            request = request.body(body);
65            request = request.header(
66                reqwest::header::CONTENT_TYPE,
67                endpoint.content_type().as_ref(),
68            );
69        }
70
71        if let Some(headers) = endpoint.headers() {
72            request = request.headers(headers);
73        }
74
75        let response = request.send().await?;
76        process_api_response(response).await
77    }
78}
79
80async fn process_api_response<T: for<'a> Deserialize<'a>>(response: Response) -> Result<T, Error> {
81    let status = response.status();
82    match status {
83        status if status.is_success() => (),
84        StatusCode::NOT_FOUND => return Err(Error::NotFound),
85        status => {
86            return Err(Error::InvalidStatusCode {
87                status_code: status,
88                message: response.text().await?,
89            })
90        }
91    }
92
93    let raw_value = response.bytes().await?;
94    let deserializer = &mut serde_json::Deserializer::from_slice(raw_value.as_ref());
95    let value: T = serde_path_to_error::deserialize(deserializer)?;
96    Ok(value)
97}