1use serde::{Deserialize, Serialize};
4use std::error::Error as StdError;
5use thiserror::Error as ThisError;
6
7pub type Result<T> = std::result::Result<T, Error>;
8pub(crate) type BoxError = Box<dyn StdError + Send + Sync>;
9
10#[derive(ThisError, Debug)]
12pub struct Error {
13 kind: Kind,
14 source: Option<BoxError>,
15 raw_response_data: Option<String>,
16}
17
18impl Error {
19 pub(crate) fn from_response_data(
20 status: Option<reqwest::StatusCode>,
21 errors: Option<ErrorResponseData>,
22 raw_response_data: Option<String>,
23 ) -> Self {
24 Self {
25 kind: Kind::ResponseStatus(ResponseStatusKind::from_code(status), errors),
26 source: None,
27 raw_response_data,
28 }
29 }
30
31 pub(crate) fn from_failed_deserialization(raw_response_data: Option<String>) -> Self {
32 Self {
33 kind: Kind::DataDeserialization,
34 source: None,
35 raw_response_data,
36 }
37 }
38
39 pub fn is_response(&self) -> bool {
41 matches!(self.kind, Kind::ResponseStatus(_, _))
42 }
43
44 pub fn is_not_found(&self) -> bool {
46 matches!(
47 self.kind,
48 Kind::ResponseStatus(ResponseStatusKind::NotFound, _)
49 )
50 }
51
52 pub fn is_bad_request(&self) -> bool {
54 matches!(
55 self.kind,
56 Kind::ResponseStatus(ResponseStatusKind::BadRequest, _)
57 )
58 }
59
60 pub fn is_body_deserialization(&self) -> bool {
63 matches!(self.kind, Kind::DataDeserialization)
64 }
65
66 pub fn get_kind(&self) -> &Kind {
68 &self.kind
69 }
70
71 pub fn get_raw_response_data(&self) -> Option<&str> {
73 self.raw_response_data.as_deref()
74 }
75}
76
77impl std::convert::From<reqwest::Error> for Error {
78 fn from(error: reqwest::Error) -> Self {
79 let kind = if error.is_body() {
80 Kind::Other("Request or response body error".into())
81 } else if error.is_builder() {
82 Kind::Other("Request builder error".into())
83 } else if error.is_connect() {
84 Kind::Connection("Connection error".into())
85 } else if error.is_decode() {
86 Kind::DataDeserialization
87 } else if error.is_redirect() {
88 Kind::Connection("Error following redirect".into())
89 } else if error.is_request() {
90 Kind::Other("Error sending request".into())
91 } else if error.is_timeout() {
92 Kind::Connection("Timeout exceeded".into())
93 } else if error.is_status() {
94 Kind::ResponseStatus(ResponseStatusKind::from_code(error.status()), None)
95 } else {
96 Kind::Other("Unknown error".into())
97 };
98
99 Self {
100 kind,
101 source: Some(error.into()),
102 raw_response_data: None,
103 }
104 }
105}
106
107impl std::convert::From<serde_json::Error> for Error {
108 fn from(error: serde_json::Error) -> Self {
109 Self {
110 kind: Kind::DataDeserialization,
111 source: Some(error.into()),
112 raw_response_data: None,
113 }
114 }
115}
116
117impl std::fmt::Display for Error {
118 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119 match &self.kind {
120 Kind::Connection(msg) => {
121 f.write_str(&format!("Connection exception. Reason: '{}'.", msg))
122 }
123 Kind::ResponseStatus(status_kind, data) => {
124 f.write_str(&format!("Negative response exception. "))?;
125 f.write_str(&format!("Status: '{:?}'. ", status_kind))?;
126
127 if let Some(data) = data {
128 let errors: Vec<&str> = data.errors.iter().map(|item| item.as_str()).collect();
129 return f.write_str(&format!("Errors: {:?}. ", errors));
130 } else if let Some(ref data) = self.raw_response_data {
131 return f.write_str(&format!("Data: '{:?}'", data));
132 }
133
134 f.write_str(".")
135 }
136 Kind::DataDeserialization => {
137 f.write_str(&format!("Body deserialization exception."))
138 }
139 Kind::Other(msg) => f.write_str(&format!("Unexpected error. Reason: '{}'.", msg)),
140 }
141 }
142}
143
144#[derive(Serialize, Deserialize, Debug)]
146pub struct ErrorResponseData {
147 errors: Vec<String>,
148}
149
150#[derive(Debug)]
152pub enum Kind {
153 Connection(String),
155 ResponseStatus(ResponseStatusKind, Option<ErrorResponseData>),
157 DataDeserialization,
159 Other(String),
161}
162
163#[derive(Debug)]
165pub enum ResponseStatusKind {
166 Unauthorized,
167 PayloadTooLarge,
168 BadRequest,
169 NotFound,
170 InternalServerError,
171 Conflict,
172 Other(Option<u16>),
173}
174
175impl ResponseStatusKind {
176 fn from_code(code: Option<reqwest::StatusCode>) -> Self {
177 if let None = code {
178 return Self::Other(None);
179 }
180
181 let code = code.unwrap();
182
183 if code.is_server_error() {
184 return Self::InternalServerError;
185 }
186
187 let code_number = code.as_u16();
188
189 match code_number {
190 401 => Self::Unauthorized,
191 413 => Self::PayloadTooLarge,
192 400 => Self::BadRequest,
193 404 => Self::NotFound,
194 409 => Self::Conflict,
195 _ => Self::Other(Some(code_number)),
196 }
197 }
198}
199
200#[cfg(test)]
201mod tests {
202 use super::*;
203
204 #[test]
205 fn is_response() {
206 let error = Error::from_response_data(Some(reqwest::StatusCode::BAD_REQUEST), None, None);
207 assert_eq!(error.is_response(), true);
208 }
209
210 #[test]
211 fn is_not_found() {
212 let error = Error::from_response_data(Some(reqwest::StatusCode::NOT_FOUND), None, None);
213 assert_eq!(error.is_not_found(), true);
214 }
215
216 #[test]
217 fn is_bad_request() {
218 let error = Error::from_response_data(Some(reqwest::StatusCode::BAD_REQUEST), None, None);
219 assert_eq!(error.is_bad_request(), true);
220 }
221
222 #[test]
223 fn is_body_deserialization() {
224 let error = Error {
225 kind: Kind::DataDeserialization,
226 source: None,
227 raw_response_data: None,
228 };
229 assert_eq!(error.is_body_deserialization(), true);
230 }
231
232 #[test]
233 fn get_kind() {
234 let error = Error::from_response_data(Some(reqwest::StatusCode::BAD_REQUEST), None, None);
235 assert!(matches!(
236 error.get_kind(),
237 Kind::ResponseStatus(ResponseStatusKind::BadRequest, None)
238 ));
239 }
240
241 #[test]
242 fn crate_response_kind_from_code_for_internal_server_error() {
243 let code = reqwest::StatusCode::BAD_GATEWAY;
244 assert!(matches!(
245 ResponseStatusKind::from_code(Some(code)),
246 ResponseStatusKind::InternalServerError,
247 ))
248 }
249
250 #[test]
251 fn crate_response_kind_from_code_for_none() {
252 assert!(matches!(
253 ResponseStatusKind::from_code(None),
254 ResponseStatusKind::Other(None),
255 ))
256 }
257
258 #[test]
259 fn crate_response_kind_from_code_for_unexpected_status() {
260 let code = reqwest::StatusCode::PROCESSING;
261 assert!(matches!(
262 ResponseStatusKind::from_code(Some(code)),
263 ResponseStatusKind::Other(Some(102)),
264 ))
265 }
266
267 #[test]
268 fn crate_response_kind_from_code_for_not_found_status() {
269 let code = reqwest::StatusCode::NOT_FOUND;
270 assert!(matches!(
271 ResponseStatusKind::from_code(Some(code)),
272 ResponseStatusKind::NotFound,
273 ))
274 }
275
276 #[test]
277 fn get_raw_response_data() {
278 let error = Error {
279 kind: Kind::DataDeserialization,
280 source: None,
281 raw_response_data: Some("<h1>Some raw response data</h1>".into()),
282 };
283
284 assert_eq!(
285 error.get_raw_response_data(),
286 Some("<h1>Some raw response data</h1>")
287 )
288 }
289}