1use reqwest::{
2 blocking::Response,
3 header::{HeaderName, HeaderValue},
4 Error as ReqwestError, StatusCode,
5};
6use serde::{de::DeserializeOwned, Deserialize};
7use serde_json::Error as JSONError;
8use std::{error::Error, fmt, io::Error as IOError};
9use thiserror::Error;
10use url::ParseError as URLParseError;
11
12#[derive(Error, Debug)]
14#[non_exhaustive]
15pub enum HttpCallError {
16 #[error("Local IO error: {0}")]
18 LocalIoError(#[from] IOError),
19
20 #[error("Invalid URL error: {0}")]
22 InvalidUrl(#[from] URLParseError),
23
24 #[error("HTTP Call error: {0}")]
26 ReqwestError(#[from] ReqwestError),
27
28 #[error("JSON decode error: {0}")]
30 JsonDecodeError(#[from] JsonDecodeError),
31
32 #[error("HTTP Status Code error: {0}")]
34 StatusCodeError(#[from] StatusCodeError),
35}
36pub type HttpCallResult<T> = Result<T, HttpCallError>;
38
39#[derive(Debug)]
41pub struct JsonDecodeError {
42 error: JSONError,
43 status_code: StatusCode,
44 request_id: Option<HeaderValue>,
45}
46
47impl JsonDecodeError {
48 pub(super) fn new(
49 error: JSONError,
50 status_code: StatusCode,
51 request_id: Option<HeaderValue>,
52 ) -> Self {
53 Self {
54 error,
55 status_code,
56 request_id,
57 }
58 }
59
60 #[inline]
62 pub fn error(&self) -> &JSONError {
63 &self.error
64 }
65
66 #[inline]
68 pub fn status_code(&self) -> StatusCode {
69 self.status_code
70 }
71
72 #[inline]
74 pub fn request_id(&self) -> Option<&HeaderValue> {
75 self.request_id.as_ref()
76 }
77}
78
79impl fmt::Display for JsonDecodeError {
80 #[inline]
81 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82 write!(
83 f,
84 "message: {}, status_code: {}",
85 self.error, self.status_code
86 )?;
87 if let Some(request_id) = &self.request_id {
88 write!(f, ", request_id: {:?}", request_id)?;
89 }
90 Ok(())
91 }
92}
93
94impl Error for JsonDecodeError {
95 #[inline]
96 fn source(&self) -> Option<&(dyn Error + 'static)> {
97 Some(&self.error)
98 }
99}
100
101#[derive(Debug)]
103pub struct StatusCodeError {
104 status_code: StatusCode,
105 error_message: Option<Box<str>>,
106 request_id: Option<HeaderValue>,
107}
108
109impl StatusCodeError {
110 #[inline]
111 pub(super) fn new(
112 status_code: StatusCode,
113 error_message: Option<Box<str>>,
114 request_id: Option<HeaderValue>,
115 ) -> Self {
116 Self {
117 status_code,
118 error_message,
119 request_id,
120 }
121 }
122
123 #[inline]
125 pub fn status_code(&self) -> StatusCode {
126 self.status_code
127 }
128
129 #[inline]
131 pub fn error_message(&self) -> Option<&str> {
132 self.error_message.as_deref()
133 }
134
135 #[inline]
137 pub fn request_id(&self) -> Option<&HeaderValue> {
138 self.request_id.as_ref()
139 }
140}
141
142impl fmt::Display for StatusCodeError {
143 #[inline]
144 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145 write!(f, "status_code: {}", self.status_code)?;
146 if let Some(error_message) = &self.error_message {
147 write!(f, ", error_message: {}", error_message)?;
148 }
149 if let Some(request_id) = &self.request_id {
150 write!(f, ", request_id: {:?}", request_id)?;
151 }
152 Ok(())
153 }
154}
155
156impl Error for StatusCodeError {}
157
158const X_REQ_ID: &str = "x-reqid";
159
160impl From<Response> for HttpCallError {
161 #[inline]
162 fn from(response: Response) -> Self {
163 #[derive(Debug, Clone, Deserialize)]
164 struct ErrorBody {
165 error: Option<Box<str>>,
166 }
167
168 let status_code = response.status();
169 let request_id = response
170 .headers()
171 .get(HeaderName::from_static(X_REQ_ID))
172 .cloned();
173
174 match serde_json::from_reader::<_, ErrorBody>(response) {
175 Ok(error_body) => Self::StatusCodeError(StatusCodeError {
176 status_code,
177 request_id,
178 error_message: error_body.error,
179 }),
180 Err(error) => Self::JsonDecodeError(JsonDecodeError {
181 error,
182 status_code,
183 request_id,
184 }),
185 }
186 }
187}
188
189pub(super) fn json_decode_response<T: DeserializeOwned>(
190 response: Response,
191) -> HttpCallResult<(T, Option<HeaderValue>)> {
192 let status_code = response.status();
193 let request_id = response
194 .headers()
195 .get(HeaderName::from_static(X_REQ_ID))
196 .cloned();
197 match serde_json::from_reader::<_, T>(response) {
198 Ok(body) => Ok((body, request_id)),
199 Err(error) => Err(HttpCallError::JsonDecodeError(JsonDecodeError {
200 error,
201 status_code,
202 request_id,
203 })),
204 }
205}