1use {crate::{content_types::{APPLICATION_JSON,
8 APPLICATION_PROTOBUF},
9 dual_format::DEBUG_FORMAT_HEADER,
10 error::{JetError,
11 Result}},
12 bytes::Bytes,
13 prost::Message,
14 reqwest::{Client,
15 Response},
16 serde::{Serialize,
17 de::DeserializeOwned},
18 std::time::Duration,
19 tracing::debug};
20
21#[derive(Clone)]
52pub struct JetClient {
53 base_url: String,
54 client: Client,
55 debug_key: Option<String>,
56}
57
58impl JetClient {
59 pub fn new(base_url: &str) -> Result<Self> {
63 let client = Client::builder().timeout(Duration::from_secs(30)).gzip(true).build()?;
64
65 Ok(Self {
66 base_url: base_url.trim_end_matches('/').to_string(),
67 client,
68 debug_key: None,
69 })
70 }
71
72 pub fn builder() -> JetClientBuilder {
74 JetClientBuilder::new()
75 }
76
77 pub async fn get<T>(&self, path: &str) -> Result<T>
83 where
84 T: Message + Default, {
85 let url = format!("{}{}", self.base_url, path);
86 debug!("GET {} (protobuf)", url);
87
88 let response = self
89 .client
90 .get(&url)
91 .header("Accept", APPLICATION_PROTOBUF)
92 .send()
93 .await?;
94
95 self.decode_protobuf_response(response).await
96 }
97
98 pub async fn post<Req, Res>(&self, path: &str, body: &Req) -> Result<Res>
100 where
101 Req: Message,
102 Res: Message + Default, {
103 let url = format!("{}{}", self.base_url, path);
104 debug!("POST {} (protobuf)", url);
105
106 let encoded = body.encode_to_vec();
107
108 let response = self
109 .client
110 .post(&url)
111 .header("Content-Type", APPLICATION_PROTOBUF)
112 .header("Accept", APPLICATION_PROTOBUF)
113 .body(encoded)
114 .send()
115 .await?;
116
117 self.decode_protobuf_response(response).await
118 }
119
120 pub async fn put<Req, Res>(&self, path: &str, body: &Req) -> Result<Res>
122 where
123 Req: Message,
124 Res: Message + Default, {
125 let url = format!("{}{}", self.base_url, path);
126 debug!("PUT {} (protobuf)", url);
127
128 let encoded = body.encode_to_vec();
129
130 let response = self
131 .client
132 .put(&url)
133 .header("Content-Type", APPLICATION_PROTOBUF)
134 .header("Accept", APPLICATION_PROTOBUF)
135 .body(encoded)
136 .send()
137 .await?;
138
139 self.decode_protobuf_response(response).await
140 }
141
142 pub async fn delete<T>(&self, path: &str) -> Result<T>
144 where
145 T: Message + Default, {
146 let url = format!("{}{}", self.base_url, path);
147 debug!("DELETE {} (protobuf)", url);
148
149 let response = self
150 .client
151 .delete(&url)
152 .header("Accept", APPLICATION_PROTOBUF)
153 .send()
154 .await?;
155
156 self.decode_protobuf_response(response).await
157 }
158
159 pub async fn post_raw(&self, path: &str, body: Bytes) -> Result<Bytes> {
161 let url = format!("{}{}", self.base_url, path);
162 debug!("POST (raw) {}", url);
163
164 let response = self
165 .client
166 .post(&url)
167 .header("Content-Type", APPLICATION_PROTOBUF)
168 .body(body)
169 .send()
170 .await?;
171
172 let bytes = response.bytes().await?;
173 Ok(bytes)
174 }
175
176 pub async fn get_json<T>(&self, path: &str) -> Result<T>
184 where
185 T: DeserializeOwned, {
186 let url = format!("{}{}", self.base_url, path);
187 debug!("GET {} (json)", url);
188
189 let mut request = self.client.get(&url).header("Accept", APPLICATION_JSON);
190
191 if let Some(key) = &self.debug_key {
192 request = request.header(DEBUG_FORMAT_HEADER, key.as_str());
193 }
194
195 let response = request.send().await?;
196 self.decode_json_response(response).await
197 }
198
199 pub async fn post_json<Req, Res>(&self, path: &str, body: &Req) -> Result<Res>
203 where
204 Req: Serialize,
205 Res: DeserializeOwned, {
206 let url = format!("{}{}", self.base_url, path);
207 debug!("POST {} (json)", url);
208
209 let json_body = serde_json::to_vec(body)?;
210
211 let mut request = self
212 .client
213 .post(&url)
214 .header("Content-Type", APPLICATION_JSON)
215 .header("Accept", APPLICATION_JSON)
216 .body(json_body);
217
218 if let Some(key) = &self.debug_key {
219 request = request.header(DEBUG_FORMAT_HEADER, key.as_str());
220 }
221
222 let response = request.send().await?;
223 self.decode_json_response(response).await
224 }
225
226 pub async fn put_json<Req, Res>(&self, path: &str, body: &Req) -> Result<Res>
228 where
229 Req: Serialize,
230 Res: DeserializeOwned, {
231 let url = format!("{}{}", self.base_url, path);
232 debug!("PUT {} (json)", url);
233
234 let json_body = serde_json::to_vec(body)?;
235
236 let mut request = self
237 .client
238 .put(&url)
239 .header("Content-Type", APPLICATION_JSON)
240 .header("Accept", APPLICATION_JSON)
241 .body(json_body);
242
243 if let Some(key) = &self.debug_key {
244 request = request.header(DEBUG_FORMAT_HEADER, key.as_str());
245 }
246
247 let response = request.send().await?;
248 self.decode_json_response(response).await
249 }
250
251 pub async fn delete_json<T>(&self, path: &str) -> Result<T>
253 where
254 T: DeserializeOwned, {
255 let url = format!("{}{}", self.base_url, path);
256 debug!("DELETE {} (json)", url);
257
258 let mut request = self.client.delete(&url).header("Accept", APPLICATION_JSON);
259
260 if let Some(key) = &self.debug_key {
261 request = request.header(DEBUG_FORMAT_HEADER, key.as_str());
262 }
263
264 let response = request.send().await?;
265 self.decode_json_response(response).await
266 }
267
268 pub async fn get_json_raw(&self, path: &str) -> Result<String> {
270 let url = format!("{}{}", self.base_url, path);
271 debug!("GET {} (json raw)", url);
272
273 let mut request = self.client.get(&url).header("Accept", APPLICATION_JSON);
274
275 if let Some(key) = &self.debug_key {
276 request = request.header(DEBUG_FORMAT_HEADER, key.as_str());
277 }
278
279 let response = request.send().await?;
280 let status = response.status();
281
282 if !status.is_success() {
283 let error_text = response.text().await.unwrap_or_default();
284 return Err(JetError::Internal(format!("HTTP {}: {}", status, error_text)));
285 }
286
287 let text = response.text().await?;
288 Ok(text)
289 }
290
291 async fn decode_protobuf_response<T>(&self, response: Response) -> Result<T>
297 where
298 T: Message + Default, {
299 let status = response.status();
300
301 if !status.is_success() {
302 let error_text = response.text().await.unwrap_or_default();
303 return Err(JetError::Internal(format!("HTTP {}: {}", status, error_text)));
304 }
305
306 let bytes = response.bytes().await?;
307 let decoded = T::decode(bytes)?;
308 Ok(decoded)
309 }
310
311 async fn decode_json_response<T>(&self, response: Response) -> Result<T>
313 where
314 T: DeserializeOwned, {
315 let status = response.status();
316
317 if !status.is_success() {
318 let error_text = response.text().await.unwrap_or_default();
319 return Err(JetError::Internal(format!("HTTP {}: {}", status, error_text)));
320 }
321
322 let bytes = response.bytes().await?;
323 let decoded = serde_json::from_slice(&bytes)?;
324 Ok(decoded)
325 }
326}
327
328pub struct JetClientBuilder {
330 base_url: Option<String>,
331 timeout: Duration,
332 gzip: bool,
333 debug_key: Option<String>,
334}
335
336impl JetClientBuilder {
337 fn new() -> Self {
338 Self {
339 base_url: None,
340 timeout: Duration::from_secs(30),
341 gzip: true,
342 debug_key: None,
343 }
344 }
345
346 pub fn base_url(mut self, url: &str) -> Self {
348 self.base_url = Some(url.trim_end_matches('/').to_string());
349 self
350 }
351
352 pub fn timeout(mut self, timeout: Duration) -> Self {
354 self.timeout = timeout;
355 self
356 }
357
358 pub fn gzip(mut self, enabled: bool) -> Self {
360 self.gzip = enabled;
361 self
362 }
363
364 pub fn debug_key(mut self, key: &str) -> Self {
369 self.debug_key = Some(key.to_string());
370 self
371 }
372
373 pub fn build(self) -> Result<JetClient> {
375 let base_url = self
376 .base_url
377 .ok_or_else(|| JetError::Internal("Base URL is required".to_string()))?;
378
379 let client = Client::builder().timeout(self.timeout).gzip(self.gzip).build()?;
380
381 Ok(JetClient {
382 base_url,
383 client,
384 debug_key: self.debug_key,
385 })
386 }
387}