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#[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}