1mod error;
2mod network_error;
3mod fetch_config;
4mod fetch_options;
5mod fetch_response;
6mod utils;
7
8use anyhow::anyhow;
9use bytes::Bytes;
10pub use error::{DeserializationError, FetchError, FetchResult, SerializationError};
11pub use network_error::NetworkError;
12pub use fetch_config::FetchConfig;
13pub use fetch_options::{ContentType, FetchOptions};
14pub use fetch_response::FetchResponse;
15pub use reqwest;
16pub use reqwest::StatusCode;
17use reqwest::{header::HeaderMap, Client, ClientBuilder, RequestBuilder, Response, Url};
18use serde::{Deserialize, Serialize};
19use std::str::FromStr;
20use std::{collections::HashMap, time::Duration};
21use utils::{map_to_reqwest_headers, reqwest_headers_to_map};
22
23pub type FetchHeaders = HashMap<String, String>;
24pub const USER_AGENT: &'static str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"));
25
26#[derive(Debug)]
27pub struct Fetch {
28 client: Client,
29 pub config: Option<FetchConfig>,
30 base_url: String,
31}
32
33impl Default for Fetch {
34 fn default() -> Self {
35 let mut headers: FetchHeaders = HashMap::new();
36 Self::insert_default_headers(&mut headers, Default::default());
37
38 Self {
39 client: ClientBuilder::default()
40 .default_headers(map_to_reqwest_headers(&headers).unwrap())
41 .build()
42 .unwrap(),
43 config: Some(FetchConfig {
44 headers: Some(headers),
45 ..Default::default()
46 }),
47 base_url: Default::default(),
48 }
49 }
50}
51
52impl Fetch {
53 pub fn new(base_url: &str, options: Option<FetchConfig>) -> FetchResult<Self> {
63 let mut options = options.unwrap_or_default();
64 let mut headers = options
65 .headers
66 .as_ref()
67 .map(|r| r.clone())
68 .unwrap_or_default();
69
70 Self::insert_default_headers(&mut headers, Some(&options));
71 options.headers = Some(headers);
72
73 let default_headers: HeaderMap;
74 let mut client = ClientBuilder::default();
75 if let Some(headers) = &options.headers {
76 default_headers = map_to_reqwest_headers(&headers)?;
77 client = client.default_headers(default_headers);
78 }
79 if let Some(timeout) = &options.timeout_ms {
80 client = client.timeout(Duration::from_millis(timeout.to_owned()))
81 }
82
83 Ok(Self {
84 base_url: base_url.to_string(),
85 config: Some(options),
86 client: client
87 .build()
88 .map_err(|e| FetchError::Unknown(anyhow!(e)))?,
89 ..Default::default()
90 })
91 }
92
93 fn insert_default_headers(headers: &mut FetchHeaders, config: Option<&FetchConfig>) {
94 headers.insert("user-agent".to_string(), USER_AGENT.to_string());
95 if let Some(config) = config {
96 headers.insert(
97 reqwest::header::CONTENT_TYPE.to_string(),
98 config.content_type.clone().to_string(),
99 );
100 headers.insert(
101 reqwest::header::ACCEPT.to_string(),
102 config.accept.clone().to_string(),
103 );
104 }
105 }
106
107 pub fn set_default_headers(&mut self, headers: Option<FetchHeaders>) -> FetchResult<()> {
119 let mut headers = headers.unwrap_or_default();
120
121 Self::insert_default_headers(&mut headers, self.config.as_ref());
122
123 let opts: FetchConfig = FetchConfig {
124 headers: Some(headers),
125 ..self.config.clone().unwrap_or(Default::default())
126 };
127
128 let new_fetch = Self::new(&self.base_url, Some(opts))?;
129 self.client = new_fetch.client;
130 self.config = new_fetch.config;
131
132 Ok(())
133 }
134
135 pub fn build_url(&self, endpoint: &str, options: Option<&FetchOptions>) -> FetchResult<Url> {
136 let mut built_string = String::new();
137 built_string += &self.base_url;
138
139 if built_string.chars().nth(built_string.chars().count() - 1) != Some('/')
140 && endpoint.chars().nth(0) != Some('/')
141 {
142 built_string += "/";
143 }
144
145 built_string += endpoint;
146 if let Some(options) = options {
147 if let Some(params) = &options.params {
148 let mut added_param = false;
149 for (index, (key, value)) in params.iter().enumerate() {
150 if !added_param {
151 built_string += "?";
152 added_param = true;
153 }
154 built_string += &format!("{key}={value}");
155 if index < params.len() - 1 {
156 built_string += "&";
157 }
158 }
159 }
160 }
161
162 let url: Url = built_string
163 .parse()
164 .map_err(|_| FetchError::InvalidUrl(built_string))?;
165
166 Ok(url)
167 }
168
169 fn make_body<U>(
170 &self,
171 data: U,
172 options: Option<&FetchOptions>,
173 ) -> FetchResult<(Vec<u8>, ContentType)>
174 where
175 U: Serialize,
176 {
177 let mut content_type: ContentType = Default::default();
178
179 if let Some(opts) = options {
180 if let Some(ref c_type) = opts.content_type {
181 content_type = c_type.clone();
182 } else {
183 if let Some(ref config) = self.config {
184 content_type = config.content_type.clone();
185 }
186 }
187 } else {
188 if let Some(ref config) = self.config {
189 content_type = config.content_type.clone();
190 }
191 }
192
193 let data_to_return = match content_type {
194 ContentType::Json => serde_json::to_vec(&data)
195 .map_err(|e| FetchError::SerializationError(SerializationError::Json(e)))?,
196 ContentType::TextXml | ContentType::ApplicationXml => serde_xml_rs::to_string(&data)
197 .map_err(|e| FetchError::SerializationError(SerializationError::Xml(e)))?
198 .into_bytes(),
199 ContentType::UrlEncoded => serde_urlencoded::to_string(&data)
200 .map_err(|e| FetchError::SerializationError(SerializationError::UrlEncoded(e)))?
201 .into_bytes(),
202 };
203
204 return Ok((data_to_return, content_type));
205 }
206
207 fn build_request<U>(
208 &self,
209 data: Option<U>,
210 options: Option<&FetchOptions>,
211 original_builder: RequestBuilder,
212 ) -> FetchResult<RequestBuilder>
213 where
214 U: Serialize,
215 {
216 let mut builder = original_builder;
217 if let Some(options) = options {
218 if let Some(headers) = &options.headers {
219 builder = builder.headers(map_to_reqwest_headers(headers)?);
220 }
221 };
222 if let Some(body) = data {
223 let (body, content_type) = self.make_body(body, options)?;
224 builder = builder.body(body);
225 builder = builder.header(reqwest::header::CONTENT_TYPE, format!("{content_type}"));
226 }
227 if let Some(opts) = options {
228 if let Some(ref accept) = opts.accept {
229 builder = builder.header(reqwest::header::ACCEPT.to_string(), accept.to_string());
230 }
231 }
232
233 return Ok(builder);
234 }
235
236 fn deserialize_response<T>(
237 &self,
238 raw_body: &Bytes,
239 content_type: ContentType,
240 ) -> FetchResult<T>
241 where
242 T: for<'de> Deserialize<'de>,
243 {
244 return match content_type {
245 ContentType::Json => Ok(serde_json::from_slice::<T>(raw_body)
246 .map_err(|e| FetchError::DeserializationError(DeserializationError::Json(e)))?),
247 ContentType::TextXml | ContentType::ApplicationXml => {
248 let body_string = String::from_utf8(raw_body.to_vec()).map_err(|_| {
249 FetchError::DeserializationError(DeserializationError::Unknown(String::from(
250 "Response body does not contain valid Utf8",
251 )))
252 })?;
253 Ok(serde_xml_rs::from_str::<T>(&body_string)
254 .map_err(|e| FetchError::DeserializationError(DeserializationError::Xml(e)))?)
255 }
256 ContentType::UrlEncoded => {
257 Ok(serde_urlencoded::from_bytes::<T>(raw_body).map_err(|e| {
258 FetchError::DeserializationError(DeserializationError::UrlEncoded(e))
259 })?)
260 }
261 };
262 }
263
264 async fn check_response_and_return_err(&self, response: Response) -> FetchResult<Response> {
265 if response.status().is_client_error() || response.status().is_server_error() {
266 return Err(FetchError::NetworkError(NetworkError::new(response).await));
267 }
268 Ok(response)
269 }
270
271 async fn response_to_fetch_response<T>(
272 &self,
273 response: Response,
274 deserialize_body: bool,
275 ) -> FetchResult<FetchResponse<T>>
276 where
277 T: for<'de> Deserialize<'de>,
278 {
279 let response = self.check_response_and_return_err(response).await?;
280 let remote_content_type = response
281 .headers()
282 .get(reqwest::header::CONTENT_TYPE)
283 .map(|c_type| {
284 c_type
285 .to_str()
286 .ok()
287 .map(|s| s.to_owned())
288 .unwrap_or_default()
289 })
290 .map(|string| ContentType::from_str(&string).ok().unwrap_or_default());
291
292 let headers = response.headers().clone();
293 let remote_address = response.remote_addr();
294 let status = response.status();
295
296 let raw_body = response.bytes().await.ok();
297 let mut body: Option<T> = None;
298
299 if let Some(raw_body) = &raw_body {
300 if deserialize_body {
301 if let Some(response_content_type) = remote_content_type {
302 body = Some(self.deserialize_response::<T>(raw_body, response_content_type)?);
303 } else {
304 body = Some(self.deserialize_response::<T>(raw_body, ContentType::Json)?);
305 }
306 }
307 }
308
309 return Ok(FetchResponse {
310 body,
311 raw_body,
312 status,
313 response_headers: reqwest_headers_to_map(&headers)?,
314 remote_address,
315 });
316 }
317
318 pub async fn post<T, U>(
365 &self,
366 endpoint: &str,
367 data: Option<U>,
368 options: Option<FetchOptions>,
369 ) -> FetchResult<FetchResponse<T>>
370 where
371 T: for<'de> Deserialize<'de>,
372 U: Serialize,
373 {
374 let options = options.unwrap_or_default();
375 let response = self
376 .build_request(
377 data,
378 Some(&options),
379 self.client.post(self.build_url(endpoint, Some(&options))?),
380 )?
381 .send()
382 .await
383 .map_err(|e| FetchError::UnableToSendRequest { err: e })?;
384
385 return Ok(self
386 .response_to_fetch_response(response, options.deserialize_body)
387 .await?);
388 }
389
390 pub async fn get<T>(
428 &self,
429 endpoint: &str,
430 options: Option<FetchOptions>,
431 ) -> FetchResult<FetchResponse<T>>
432 where
433 T: for<'de> Deserialize<'de>,
434 {
435 let options = options.unwrap_or_default();
436 let response = self
437 .build_request::<()>(
438 None,
439 Some(&options),
440 self.client.get(self.build_url(endpoint, Some(&options))?),
441 )?
442 .send()
443 .await
444 .map_err(|e| FetchError::UnableToSendRequest { err: e })?;
445
446 return Ok(self
447 .response_to_fetch_response(response, options.deserialize_body)
448 .await?);
449 }
450
451 pub async fn delete<T, U>(
482 &self,
483 endpoint: &str,
484 data: Option<T>,
485 options: Option<FetchOptions>,
486 ) -> FetchResult<FetchResponse<U>>
487 where
488 T: Serialize,
489 U: for<'de> Deserialize<'de>,
490 {
491 let options = options.unwrap_or_default();
492 let response = self
493 .build_request(
494 data,
495 Some(&options),
496 self.client
497 .delete(self.build_url(endpoint, Some(&options))?),
498 )?
499 .send()
500 .await
501 .map_err(|e| FetchError::UnableToSendRequest { err: e })?;
502
503 return Ok(self
504 .response_to_fetch_response(response, options.deserialize_body)
505 .await?);
506 }
507
508 pub async fn put<T, U>(
539 &self,
540 endpoint: &str,
541 data: Option<T>,
542 options: Option<FetchOptions>,
543 ) -> FetchResult<FetchResponse<U>>
544 where
545 T: Serialize,
546 U: for<'de> Deserialize<'de>,
547 {
548 let options = options.unwrap_or_default();
549 let response = self
550 .build_request(
551 data,
552 Some(&options),
553 self.client.put(self.build_url(endpoint, Some(&options))?),
554 )?
555 .send()
556 .await
557 .map_err(|e| FetchError::UnableToSendRequest { err: e })?;
558
559 return Ok(self
560 .response_to_fetch_response(response, options.deserialize_body)
561 .await?);
562 }
563
564 pub async fn patch<T, U>(
595 &self,
596 endpoint: &str,
597 data: Option<T>,
598 options: Option<FetchOptions>,
599 ) -> FetchResult<FetchResponse<U>>
600 where
601 T: Serialize,
602 U: for<'de> Deserialize<'de>,
603 {
604 let options = options.unwrap_or_default();
605 let response = self
606 .build_request(
607 data,
608 Some(&options),
609 self.client.patch(self.build_url(endpoint, Some(&options))?),
610 )?
611 .send()
612 .await
613 .map_err(|e| FetchError::UnableToSendRequest { err: e })?;
614
615 return Ok(self
616 .response_to_fetch_response(response, options.deserialize_body)
617 .await?);
618 }
619}