1use reqwest::{Client, RequestBuilder, Response};
8use serde::de::DeserializeOwned;
9use thiserror::Error;
10
11use crate::{FromApiState, Queryable, RequestMethod};
12
13#[derive(Debug, Error)]
15pub enum ApiError {
16 #[error("An error happened with serialization: {0}")]
18 Serde(serde_json::Error),
19
20 #[error("An error happened with the request itself: {0}")]
22 Reqwest(reqwest::Error),
23}
24
25impl From<serde_json::Error> for ApiError {
26 fn from(err: serde_json::Error) -> Self { Self::Serde(err) }
27}
28
29impl From<reqwest::Error> for ApiError {
30 fn from(err: reqwest::Error) -> Self { Self::Reqwest(err) }
31}
32
33#[async_trait::async_trait]
35pub trait ApiClient<State> {
36 fn state(&self) -> &State;
38
39 fn client(&self) -> &Client;
41
42 async fn before_request(
47 &self, req: RequestBuilder,
48 ) -> Result<RequestBuilder, ApiError> {
49 Ok(req)
50 }
51
52 async fn handle_response<ReturnType, FromState, QueryableType>(
60 &self, queryable: QueryableType, response: Response,
61 ) -> Result<ReturnType, ApiError>
62 where
63 ReturnType: DeserializeOwned,
64 FromState: FromApiState<State>,
65 QueryableType: Queryable<FromState, ReturnType> + Send + Sync,
66 {
67 let response = response.error_for_status()?;
68 let val = response.bytes().await?;
69 Ok(queryable.deserialize(&val)?)
70 }
71
72 async fn query<ReturnType, FromState, QueryableType>(
74 &self, queryable: QueryableType,
75 ) -> Result<ReturnType, ApiError>
76 where
77 ReturnType: DeserializeOwned,
78 FromState: FromApiState<State>,
79 QueryableType: Queryable<FromState, ReturnType> + Send + Sync,
80 {
81 let request = Self::build_request(
82 self.client(),
83 FromState::from_state(self.state()),
84 &queryable,
85 )?;
86 let request = self.before_request(request).await?;
87 let response = request.send().await?;
88
89 self.handle_response(queryable, response).await
90 }
91
92 fn build_request<ReturnType, FromState, QueryableType>(
98 http: &Client, api_state: &FromState, queryable: &QueryableType,
99 ) -> Result<RequestBuilder, ApiError>
100 where
101 ReturnType: DeserializeOwned,
102 FromState: FromApiState<State>,
103 QueryableType: Queryable<FromState, ReturnType> + Send + Sync,
104 {
105 let mut request = http.request(
106 match queryable.method(api_state) {
107 RequestMethod::Get => reqwest::Method::GET,
108 RequestMethod::Head => reqwest::Method::HEAD,
109 RequestMethod::Patch => reqwest::Method::PATCH,
110 RequestMethod::Post => reqwest::Method::POST,
111 RequestMethod::Put => reqwest::Method::PUT,
112 RequestMethod::Delete => reqwest::Method::DELETE,
113 },
114 queryable.url(api_state),
115 );
116 if let Some(body) = queryable.body(api_state) {
117 request = request.body(body?).header("Content-Type", "application/json");
118 }
119
120 Ok(request)
121 }
122}