1#![cfg_attr(feature = "fail-on-warnings", deny(warnings))]
2#![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)]
3#![allow(clippy::multiple_crate_versions)]
4
5use std::{collections::BTreeMap, marker::PhantomData};
6
7use async_trait::async_trait;
8use bytes::Bytes;
9use strum::{AsRefStr, EnumString};
10use switchy_http_models::{Method, StatusCode};
11use thiserror::Error;
12
13pub use switchy_http_models as models;
14
15#[cfg(feature = "reqwest")]
16pub mod reqwest;
17
18#[cfg(feature = "simulator")]
19pub mod simulator;
20
21#[derive(Debug, Error)]
22pub enum Error {
23 #[error("Decode")]
24 Decode,
25
26 #[cfg(feature = "json")]
27 #[error(transparent)]
28 Deserialize(#[from] serde_json::Error),
29
30 #[cfg(feature = "reqwest")]
31 #[error(transparent)]
32 Reqwest(#[from] ::reqwest::Error),
33}
34
35#[derive(Debug, Clone, Copy, EnumString, AsRefStr)]
36#[strum(serialize_all = "kebab-case")]
37pub enum Header {
38 Authorization,
39 UserAgent,
40 Range,
41 ContentLength,
42}
43
44#[async_trait]
45pub trait GenericRequestBuilder<R>: Send + Sync {
46 fn header(&mut self, name: &str, value: &str);
47 #[allow(unused)]
48 fn body(&mut self, body: Bytes);
49 #[cfg(feature = "json")]
50 fn form(&mut self, form: &serde_json::Value);
51 async fn send(&mut self) -> Result<R, Error>;
52}
53
54pub trait GenericClientBuilder<RB, C: GenericClient<RB>>: Send + Sync {
55 fn build(self) -> Result<C, Error>;
59}
60
61pub trait GenericClient<RB>: Send + Sync {
62 fn get(&self, url: &str) -> RB {
63 self.request(Method::Get, url)
64 }
65
66 fn post(&self, url: &str) -> RB {
67 self.request(Method::Post, url)
68 }
69
70 fn put(&self, url: &str) -> RB {
71 self.request(Method::Put, url)
72 }
73
74 fn patch(&self, url: &str) -> RB {
75 self.request(Method::Patch, url)
76 }
77
78 fn delete(&self, url: &str) -> RB {
79 self.request(Method::Delete, url)
80 }
81
82 fn head(&self, url: &str) -> RB {
83 self.request(Method::Head, url)
84 }
85
86 fn options(&self, url: &str) -> RB {
87 self.request(Method::Options, url)
88 }
89
90 fn request(&self, method: Method, url: &str) -> RB;
91}
92
93#[async_trait]
94pub trait GenericResponse: Send + Sync {
95 fn status(&self) -> StatusCode;
96 fn headers(&mut self) -> &BTreeMap<String, String>;
97 async fn text(&mut self) -> Result<String, Error>;
98 async fn bytes(&mut self) -> Result<Bytes, Error>;
99 #[cfg(feature = "stream")]
100 fn bytes_stream(
101 &mut self,
102 ) -> std::pin::Pin<Box<dyn futures_core::Stream<Item = Result<Bytes, Error>> + Send>>;
103}
104
105pub struct RequestBuilderWrapper<R, B: GenericRequestBuilder<R>>(
106 pub(crate) B,
107 pub(crate) PhantomData<R>,
108);
109pub struct ClientWrapper<RB, T: GenericClient<RB>>(pub(crate) T, pub(crate) PhantomData<RB>);
110pub struct ClientBuilderWrapper<RB, C: GenericClient<RB>, T: GenericClientBuilder<RB, C>>(
111 pub(crate) T,
112 PhantomData<RB>,
113 PhantomData<C>,
114);
115pub struct ResponseWrapper<T: GenericResponse>(pub(crate) T);
116
117#[allow(unused)]
118macro_rules! impl_http {
119 ($module:ident, $local_module:ident $(,)?) => {
120 paste::paste! {
121 pub use [< impl_ $module >]::*;
122 }
123
124 mod $local_module {
125 use crate::*;
126
127 paste::paste! {
128 pub type [< $module:camel Response >] = ResponseWrapper<$module::Response>;
129 type ModuleResponse = [< $module:camel Response >];
130
131 pub type [< $module:camel RequestBuilder >] = RequestBuilderWrapper<ModuleResponse, $module::RequestBuilder>;
132 type ModuleRequestBuilder = [< $module:camel RequestBuilder >];
133
134 pub type [< $module:camel Client >] = ClientWrapper<ModuleRequestBuilder, $module::Client>;
135 type ModuleClient = [< $module:camel Client >];
136
137 pub type [< $module:camel ClientBuilder >] = ClientBuilderWrapper<ModuleRequestBuilder, ModuleClient, $module::ClientBuilder>;
138 type ModuleClientBuilder = [< $module:camel ClientBuilder >];
139 }
140
141 impl ModuleRequestBuilder {
142 #[must_use]
143 pub fn header(mut self, name: &str, value: &str) -> Self {
144 self.0.header(name, value);
145 self
146 }
147
148 pub async fn send(mut self) -> Result<ModuleResponse, Error> {
153 self.0.send().await
154 }
155 }
156
157 #[async_trait]
158 impl GenericRequestBuilder<ModuleResponse> for ModuleRequestBuilder {
159 fn header(&mut self, name: &str, value: &str) {
160 self.0.header(name, value);
161 }
162
163 fn body(&mut self, body: Bytes) {
164 self.0.body(body);
165 }
166
167 #[cfg(feature = "json")]
168 fn form(&mut self, form: &serde_json::Value) {
169 self.0.form(form);
170 }
171
172 async fn send(&mut self) -> Result<ModuleResponse, Error> {
173 self.0.send().await
174 }
175 }
176
177 #[cfg(feature = "json")]
178 impl ModuleRequestBuilder {
179 #[must_use]
183 pub fn json<T: serde::Serialize + ?Sized>(mut self, body: &T) -> Self {
184 let mut bytes: Vec<u8> = Vec::new();
185 serde_json::to_writer(&mut bytes, body).unwrap();
186 <Self as GenericRequestBuilder<ModuleResponse>>::body(&mut self, bytes.into());
187 self
188 }
189
190 #[must_use]
194 pub fn form<T: serde::Serialize + ?Sized>(mut self, form: &T) -> Self {
195 let value = serde_json::to_value(form).unwrap();
196 <Self as GenericRequestBuilder<ModuleResponse>>::form(&mut self, &value);
197 self
198 }
199 }
200
201 #[async_trait]
202 impl GenericResponse for ModuleResponse {
203 #[must_use]
204 fn status(&self) -> StatusCode {
205 self.0.status()
206 }
207
208 #[must_use]
209 fn headers(&mut self) -> &BTreeMap<String, String> {
210 self.0.headers()
211 }
212
213 #[must_use]
214 async fn text(&mut self) -> Result<String, Error> {
215 self.0.text().await
216 }
217
218 #[must_use]
219 async fn bytes(&mut self) -> Result<Bytes, Error> {
220 self.0.bytes().await
221 }
222
223 #[must_use]
224 #[cfg(feature = "stream")]
225 fn bytes_stream(
226 &mut self,
227 ) -> std::pin::Pin<Box<dyn futures_core::Stream<Item = Result<Bytes, Error>> + Send>>
228 {
229 self.0.bytes_stream()
230 }
231 }
232
233 impl ModuleResponse {
234 #[must_use]
235 pub fn status(&self) -> StatusCode {
236 <Self as GenericResponse>::status(self)
237 }
238
239 #[must_use]
240 pub fn headers(&mut self) -> &BTreeMap<String, String> {
241 <Self as GenericResponse>::headers(self)
242 }
243
244 pub async fn text(mut self) -> Result<String, Error> {
248 <Self as GenericResponse>::text(&mut self).await
249 }
250
251 pub async fn bytes(mut self) -> Result<Bytes, Error> {
255 <Self as GenericResponse>::bytes(&mut self).await
256 }
257 }
258
259 impl GenericClientBuilder<ModuleRequestBuilder, ModuleClient> for ModuleClientBuilder {
260 fn build(self) -> Result<ModuleClient, Error> {
261 self.0.build()
262 }
263 }
264
265 impl ModuleClientBuilder {
266 pub fn build(self) -> Result<ModuleClient, Error> {
270 <Self as GenericClientBuilder<ModuleRequestBuilder, ModuleClient>>::build(self)
271 }
272 }
273
274 impl ModuleResponse {
275 #[cfg(feature = "stream")]
279 pub fn bytes_stream(
280 mut self,
281 ) -> impl futures_core::Stream<Item = Result<Bytes, Error>> {
282 <Self as GenericResponse>::bytes_stream(&mut self)
283 }
284 }
285
286 impl ModuleResponse {
287 #[cfg(feature = "json")]
291 pub async fn json<T: serde::de::DeserializeOwned>(mut self) -> Result<T, Error> {
292 let bytes = <Self as GenericResponse>::bytes(&mut self).await?;
293 Ok(serde_json::from_slice(&bytes)?)
294 }
295 }
296
297 impl Default for ModuleClient {
298 fn default() -> Self {
299 Self::new()
300 }
301 }
302
303 impl ModuleClient {
304 #[must_use]
308 pub fn new() -> Self {
309 Self::builder().0.build().unwrap()
310 }
311
312 #[must_use]
313 pub const fn builder() -> ModuleClientBuilder {
314 ModuleClientBuilder::new()
315 }
316
317 #[must_use]
318 pub fn get(&self, url: &str) -> ModuleRequestBuilder {
319 <Self as GenericClient<ModuleRequestBuilder>>::get(self, url)
320 }
321
322 #[must_use]
323 pub fn post(&self, url: &str) -> ModuleRequestBuilder {
324 <Self as GenericClient<ModuleRequestBuilder>>::post(self, url)
325 }
326
327 #[must_use]
328 pub fn put(&self, url: &str) -> ModuleRequestBuilder {
329 <Self as GenericClient<ModuleRequestBuilder>>::put(self, url)
330 }
331
332 #[must_use]
333 pub fn patch(&self, url: &str) -> ModuleRequestBuilder {
334 <Self as GenericClient<ModuleRequestBuilder>>::patch(self, url)
335 }
336
337 #[must_use]
338 pub fn delete(&self, url: &str) -> ModuleRequestBuilder {
339 <Self as GenericClient<ModuleRequestBuilder>>::delete(self, url)
340 }
341
342 #[must_use]
343 pub fn head(&self, url: &str) -> ModuleRequestBuilder {
344 <Self as GenericClient<ModuleRequestBuilder>>::head(self, url)
345 }
346
347 #[must_use]
348 pub fn options(&self, url: &str) -> ModuleRequestBuilder {
349 <Self as GenericClient<ModuleRequestBuilder>>::options(self, url)
350 }
351
352 #[must_use]
353 pub fn request(&self, method: Method, url: &str) -> ModuleRequestBuilder {
354 <Self as GenericClient<ModuleRequestBuilder>>::request(self, method, url)
355 }
356 }
357
358 impl Default for ModuleClientBuilder {
359 fn default() -> Self {
360 Self::new()
361 }
362 }
363
364 impl GenericClient<ModuleRequestBuilder> for ModuleClient {
365 fn request(&self, method: Method, url: &str) -> ModuleRequestBuilder {
366 self.0.request(method, url)
367 }
368 }
369 }
370 };
371}
372
373#[cfg(feature = "simulator")]
374impl_http!(simulator, impl_simulator);
375
376#[cfg(feature = "reqwest")]
377impl_http!(reqwest, impl_reqwest);
378
379#[allow(unused)]
380macro_rules! impl_gen_types {
381 ($module:ident $(,)?) => {
382 paste::paste! {
383 pub type RequestBuilder = [< $module:camel RequestBuilder >];
384 pub type Client = [< $module:camel Client >];
385 pub type Response = [< $module:camel Response >];
386 }
387 };
388}
389
390#[cfg(feature = "simulator")]
391impl_gen_types!(simulator);
392
393#[cfg(all(not(feature = "simulator"), feature = "reqwest"))]
394impl_gen_types!(reqwest);