Skip to main content

imei_info/
error.rs

1use std::error::Error;
2use std::fmt::Display;
3
4use reqwest::{Error as ReqwestError, Response, StatusCode};
5
6use crate::api::{
7    ServiceCheckInvalidApiKeyResponseBody, ServiceCheckPendingResponseBody,
8    ServiceCheckStandardResponseBody,
9};
10
11pub(crate) type Result<T> = std::result::Result<T, ServiceCheckError>;
12
13// TODO: Maybe split these into enum with `Wrapper` and `Api` variants
14#[derive(Debug)]
15pub enum ServiceCheckError {
16    RequestPending { history_id: String, ulid: String },
17    InvalidImeiNumber,
18    MissingApiKey,
19    InvalidApiKey { detail: String },
20    InvalidServiceID,
21    UnknownRequestError { error: ReqwestError },
22    UnknownApiError { error: Response },
23}
24
25impl PartialEq for ServiceCheckError {
26    fn eq(&self, other: &Self) -> bool {
27        match (self, other) {
28            (
29                ServiceCheckError::RequestPending {
30                    history_id: history_id_self,
31                    ulid: ulid_self,
32                },
33                ServiceCheckError::RequestPending {
34                    history_id: history_id_other,
35                    ulid: ulid_other,
36                },
37            ) => history_id_self.eq(history_id_other) && ulid_self.eq(ulid_other),
38            (ServiceCheckError::InvalidImeiNumber, ServiceCheckError::InvalidImeiNumber) => true,
39            (ServiceCheckError::MissingApiKey, ServiceCheckError::MissingApiKey) => true,
40            (
41                ServiceCheckError::InvalidApiKey {
42                    detail: detail_self,
43                },
44                ServiceCheckError::InvalidApiKey {
45                    detail: detail_other,
46                },
47            ) => detail_self.eq(detail_other),
48            (ServiceCheckError::InvalidServiceID, ServiceCheckError::InvalidServiceID) => true,
49            (
50                ServiceCheckError::UnknownRequestError { error: error_self },
51                ServiceCheckError::UnknownRequestError { error: error_other },
52            ) => format!("{:?}", error_self) == format!("{:?}", error_other),
53            (
54                ServiceCheckError::UnknownApiError { error: error_self },
55                ServiceCheckError::UnknownApiError { error: error_other },
56            ) => format!("{:?}", error_self) == format!("{:?}", error_other),
57            _ => false,
58        }
59    }
60}
61
62impl Error for ServiceCheckError {}
63
64impl Display for ServiceCheckError {
65    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66        f.write_str(match self {
67            ServiceCheckError::RequestPending { .. } => {
68                "request has not resolved yet and is pending"
69            }
70            ServiceCheckError::InvalidImeiNumber => {
71                "IMEI or TAC number passed to wrapper is invalid"
72            }
73            ServiceCheckError::MissingApiKey => "API key was not provided",
74            ServiceCheckError::InvalidApiKey { .. } => "API key is invalid",
75            ServiceCheckError::InvalidServiceID => "service ID is invalid",
76            ServiceCheckError::UnknownRequestError { .. } => "unknown error occurred with request",
77            ServiceCheckError::UnknownApiError { .. } => {
78                "unknown error occurred with API; wrapper may be out-of-date"
79            }
80        })
81    }
82}
83
84impl From<ReqwestError> for ServiceCheckError {
85    fn from(error: ReqwestError) -> Self {
86        ServiceCheckError::UnknownRequestError { error }
87    }
88}
89
90impl ServiceCheckError {
91    pub(crate) async fn classify_response(
92        response: Response,
93    ) -> Result<ServiceCheckStandardResponseBody> {
94        match response.status() {
95            StatusCode::OK => Ok(response
96                .json::<ServiceCheckStandardResponseBody>()
97                .await
98                .unwrap()),
99            StatusCode::ACCEPTED => {
100                let ServiceCheckPendingResponseBody {
101                    history_id, ulid, ..
102                } = response
103                    .json::<ServiceCheckPendingResponseBody>()
104                    .await
105                    .unwrap();
106                Err(ServiceCheckError::RequestPending { history_id, ulid })
107            }
108            StatusCode::FORBIDDEN => Err(ServiceCheckError::MissingApiKey),
109            StatusCode::UNAUTHORIZED => {
110                let ServiceCheckInvalidApiKeyResponseBody { detail } = response
111                    .json::<ServiceCheckInvalidApiKeyResponseBody>()
112                    .await
113                    .unwrap();
114                Err(ServiceCheckError::InvalidApiKey { detail })
115            }
116            StatusCode::NOT_FOUND => Err(ServiceCheckError::InvalidServiceID),
117            _ => Err(ServiceCheckError::UnknownApiError { error: response }),
118        }
119    }
120}