1use std::marker::PhantomData;
2
3use reqwest::{Client, Response};
4use serde::de::DeserializeOwned;
5use url::Url;
6
7use crate::ApiError;
8
9pub trait QueryBuilder: Sized {
11 fn add_query(&mut self, key: String, value: String);
13
14 fn query(mut self, key: impl Into<String>, value: impl ToString) -> Self {
16 self.add_query(key.into(), value.to_string());
17 self
18 }
19
20 fn query_opt(mut self, key: impl Into<String>, value: Option<impl ToString>) -> Self {
22 if let Some(v) = value {
23 self.add_query(key.into(), v.to_string());
24 }
25 self
26 }
27
28 fn query_many<I, V>(self, key: impl Into<String>, values: I) -> Self
30 where
31 I: IntoIterator<Item = V>,
32 V: ToString,
33 {
34 let key = key.into();
35 let mut result = self;
36 for value in values {
37 result.add_query(key.clone(), value.to_string());
38 }
39 result
40 }
41
42 fn query_many_opt<I, V>(self, key: impl Into<String>, values: Option<I>) -> Self
44 where
45 I: IntoIterator<Item = V>,
46 V: ToString,
47 {
48 if let Some(values) = values {
49 self.query_many(key, values)
50 } else {
51 self
52 }
53 }
54}
55
56pub trait RequestError: From<ApiError> + std::fmt::Debug {
58 fn from_response(response: Response) -> impl std::future::Future<Output = Self> + Send;
60}
61
62pub struct Request<T, E> {
64 pub(crate) client: Client,
65 pub(crate) base_url: Url,
66 pub(crate) path: String,
67 pub(crate) query: Vec<(String, String)>,
68 pub(crate) _marker: PhantomData<(T, E)>,
69}
70
71impl<T, E> Request<T, E> {
72 pub fn new(client: Client, base_url: Url, path: impl Into<String>) -> Self {
74 Self {
75 client,
76 base_url,
77 path: path.into(),
78 query: Vec::new(),
79 _marker: PhantomData,
80 }
81 }
82}
83
84impl<T, E> QueryBuilder for Request<T, E> {
85 fn add_query(&mut self, key: String, value: String) {
86 self.query.push((key, value));
87 }
88}
89
90impl<T: DeserializeOwned, E: RequestError> Request<T, E> {
91 pub async fn send(self) -> Result<T, E> {
93 let response = self.send_raw().await?;
94
95 let text = response
97 .text()
98 .await
99 .map_err(|e| E::from(ApiError::from(e)))?;
100
101 tracing::debug!("Response body: {}", text);
102
103 serde_json::from_str(&text).map_err(|e| {
105 tracing::error!("Deserialization failed: {}", e);
106 tracing::error!("Failed to deserialize: {}", text);
107 E::from(ApiError::from(e))
108 })
109 }
110
111 pub async fn send_raw(self) -> Result<Response, E> {
113 let url = self
114 .base_url
115 .join(&self.path)
116 .map_err(|e| E::from(ApiError::from(e)))?;
117
118 let mut request = self.client.get(url);
119
120 if !self.query.is_empty() {
121 request = request.query(&self.query);
122 }
123
124 tracing::debug!("Sending request to: {:?}", request);
125
126 let response = request
127 .send()
128 .await
129 .map_err(|e| E::from(ApiError::from(e)))?;
130 let status = response.status();
131
132 tracing::debug!("Response status: {}", status);
133
134 if !status.is_success() {
135 let error = E::from_response(response).await;
136 tracing::error!("Request failed: {:?}", error);
137 return Err(error);
138 }
139
140 Ok(response)
141 }
142}
143
144pub struct TypedRequest<T> {
146 pub(crate) _marker: PhantomData<T>,
147}
148
149impl<T> TypedRequest<T> {
150 pub fn new() -> Self {
151 Self {
152 _marker: PhantomData,
153 }
154 }
155}
156
157impl<T> Default for TypedRequest<T> {
158 fn default() -> Self {
159 Self::new()
160 }
161}