deta_rust/
error.rs

1//! When performing an action in the deta drive API or deserializing the response fails.
2
3use 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/// A type representing all possible failures that may occur during integration with deta.
11#[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    /// Checks whether the error is caused by any unsuccessful response status.
40    pub fn is_response(&self) -> bool {
41        matches!(self.kind, Kind::ResponseStatus(_, _))
42    }
43
44    /// Checks whether the error is caused by the 404 response status.
45    pub fn is_not_found(&self) -> bool {
46        matches!(
47            self.kind,
48            Kind::ResponseStatus(ResponseStatusKind::NotFound, _)
49        )
50    }
51
52    /// Checks whether the error is caused by the 400 response status.
53    pub fn is_bad_request(&self) -> bool {
54        matches!(
55            self.kind,
56            Kind::ResponseStatus(ResponseStatusKind::BadRequest, _)
57        )
58    }
59
60    /// Case if the error is due to deserialization of the response for **successful** completion of the task.
61    /// The failure to deserialise the response for an incorrect status will never result in this error.
62    pub fn is_body_deserialization(&self) -> bool {
63        matches!(self.kind, Kind::DataDeserialization)
64    }
65
66    /// Returns a reference to the [`Kind`](Kind) enum.
67    pub fn get_kind(&self) -> &Kind {
68        &self.kind
69    }
70
71    /// Returns raw deta's response body, if exists.
72    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/// Body for the responses of stasus 400 or 404.
145#[derive(Serialize, Deserialize, Debug)]
146pub struct ErrorResponseData {
147    errors: Vec<String>,
148}
149
150/// Identifies the cause of failure.
151#[derive(Debug)]
152pub enum Kind {
153    ///Inability to establish a connection.
154    Connection(String),
155    /// Negative response from the server.
156    ResponseStatus(ResponseStatusKind, Option<ErrorResponseData>),
157    /// The response body for a correctly performed task cannot be deserialized.
158    DataDeserialization,
159    /// Unknown cause. Check source method.
160    Other(String),
161}
162
163/// Identifies common causes of errors from server responses.
164#[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}