1use std::collections::BTreeMap;
2
3use cdumay_error::{ErrorBuilder, ErrorKind, GenericErrors};
4use serde_json::value::Value;
5
6#[derive(Debug)]
7pub struct ResponseErrorWithContext {
8 pub status: reqwest::StatusCode,
9 pub context: Option<BTreeMap<String, Value>>,
10}
11
12
13#[derive(Debug)]
14pub enum ClientError {
15 InternalError(String),
16 ParseError(String),
17 GenericHttpError(reqwest::Error),
18 UrlError(url::ParseError),
19 ResponseError(ResponseErrorWithContext),
20 InvalidHeaderName(reqwest::header::InvalidHeaderName),
21 InvalidHeaderValue(reqwest::header::InvalidHeaderValue),
22}
23
24impl From<ClientError> for ErrorBuilder {
25 fn from(value: ClientError) -> Self {
26 match value {
27 ClientError::InternalError(data) => ErrorBuilder::from(GenericErrors::UNKNOWN_ERROR).message(data),
28 ClientError::ParseError(data) => ErrorBuilder::from(GenericErrors::DESERIALIZATION_ERROR).message(data),
29 ClientError::GenericHttpError(err) => ErrorBuilder::from(HttpStatusCodeErrors::GENERIC_HTTP_CLIENT_ERROR).message(err.to_string()),
30 ClientError::UrlError(err) => ErrorBuilder::from(HttpStatusCodeErrors::INVALID_URL).message(err.to_string()),
31 ClientError::ResponseError(err) => {
32 match err.context {
33 Some(context) => ErrorBuilder::from(HttpStatusCodeErrors::from_status(err.status)).extra(context.into()),
34 None => ErrorBuilder::from(HttpStatusCodeErrors::from_status(err.status))
35 }
36 }
37 ClientError::InvalidHeaderName(err) => ErrorBuilder::from(GenericErrors::VALIDATION_ERROR).message(err.to_string()),
38 ClientError::InvalidHeaderValue(err) => ErrorBuilder::from(GenericErrors::VALIDATION_ERROR).message(err.to_string()),
39 }
40 }
41}
42
43impl From<reqwest::Error> for ClientError {
44 fn from(err: reqwest::Error) -> ClientError {
45 ClientError::GenericHttpError(err)
46 }
47}
48
49impl From<reqwest::header::InvalidHeaderName> for ClientError {
50 fn from(err: reqwest::header::InvalidHeaderName) -> ClientError {
51 ClientError::InvalidHeaderName(err)
52 }
53}
54
55impl From<reqwest::header::InvalidHeaderValue> for ClientError {
56 fn from(err: reqwest::header::InvalidHeaderValue) -> ClientError {
57 ClientError::InvalidHeaderValue(err)
58 }
59}
60
61impl From<reqwest::blocking::Response> for ClientError {
62 fn from(resp: reqwest::blocking::Response) -> ClientError {
63 ClientError::ResponseError(ResponseErrorWithContext {
64 status: resp.status(),
65 context: resp.json::<BTreeMap<String, Value>>().ok(),
66 })
67 }
68}
69
70
71pub struct HttpStatusCodeErrors;
72
73impl HttpStatusCodeErrors {
74 pub const INVALID_URL: ErrorKind = ErrorKind("Err-27071", 400, "Invalid url");
75 pub const GENERIC_HTTP_CLIENT_ERROR: ErrorKind = ErrorKind("Err-05192", 500, "Generic HTTP client error");
76 pub const MULTIPLE_CHOICES: ErrorKind = ErrorKind("Err-11298", 300, "Multiple Choices");
77 pub const MOVED_PERMANENTLY: ErrorKind = ErrorKind("Err-23108", 301, "Moved Permanently");
78 pub const FOUND: ErrorKind = ErrorKind("Err-07132", 302, "Found");
79 pub const SEE_OTHER: ErrorKind = ErrorKind("Err-16746", 303, "See Other");
80 pub const NOT_MODIFIED: ErrorKind = ErrorKind("Err-21556", 304, "Not Modified");
81 pub const USE_PROXY: ErrorKind = ErrorKind("Err-31839", 305, "Use Proxy");
82 pub const TEMPORARY_REDIRECT: ErrorKind = ErrorKind("Err-25446", 307, "Temporary Redirect");
83 pub const PERMANENT_REDIRECT: ErrorKind = ErrorKind("Err-12280", 308, "Permanent Redirect");
84 pub const BAD_REQUEST: ErrorKind = ErrorKind("Err-26760", 400, "Bad Request");
85 pub const UNAUTHORIZED: ErrorKind = ErrorKind("Err-08059", 401, "Unauthorized");
86 pub const PAYMENT_REQUIRED: ErrorKind = ErrorKind("Err-18076", 402, "Payment Required");
87 pub const FORBIDDEN: ErrorKind = ErrorKind("Err-23134", 403, "Forbidden");
88 pub const NOT_FOUND: ErrorKind = ErrorKind("Err-18430", 404, "Not Found");
89 pub const METHOD_NOT_ALLOWED: ErrorKind = ErrorKind("Err-23585", 405, "Method Not Allowed");
90 pub const NOT_ACCEPTABLE: ErrorKind = ErrorKind("Err-04289", 406, "Not Acceptable");
91 pub const PROXY_AUTHENTICATION_REQUIRED: ErrorKind = ErrorKind("Err-17336", 407, "Proxy Authentication Required");
92 pub const REQUEST_TIMEOUT: ErrorKind = ErrorKind("Err-00565", 408, "Request Timeout");
93 pub const CONFLICT: ErrorKind = ErrorKind("Err-08442", 409, "Conflict");
94 pub const GONE: ErrorKind = ErrorKind("Err-19916", 410, "Gone");
95 pub const LENGTH_REQUIRED: ErrorKind = ErrorKind("Err-09400", 411, "Length Required");
96 pub const PRECONDITION_FAILED: ErrorKind = ErrorKind("Err-22509", 412, "Precondition Failed");
97 pub const PAYLOAD_TOO_LARGE: ErrorKind = ErrorKind("Err-10591", 413, "Payload Too Large");
98 pub const URI_TOO_LONG: ErrorKind = ErrorKind("Err-01377", 414, "URI Too Long");
99 pub const UNSUPPORTED_MEDIA_TYPE: ErrorKind = ErrorKind("Err-12512", 415, "Unsupported Media Type");
100 pub const RANGE_NOT_SATISFIABLE: ErrorKind = ErrorKind("Err-21696", 416, "Range Not Satisfiable");
101 pub const EXPECTATION_FAILED: ErrorKind = ErrorKind("Err-16872", 417, "Expectation Failed");
102 pub const IM_A_TEAPOT: ErrorKind = ErrorKind("Err-23719", 418, "I'm a teapot");
103 pub const MISDIRECTED_REQUEST: ErrorKind = ErrorKind("Err-26981", 421, "Misdirected Request");
104 pub const UNPROCESSABLE_ENTITY: ErrorKind = ErrorKind("Err-12568", 422, "Unprocessable Entity");
105 pub const LOCKED: ErrorKind = ErrorKind("Err-32695", 423, "Locked");
106 pub const FAILED_DEPENDENCY: ErrorKind = ErrorKind("Err-19693", 424, "Failed Dependency");
107 pub const UPGRADE_REQUIRED: ErrorKind = ErrorKind("Err-22991", 426, "Upgrade Required");
108 pub const PRECONDITION_REQUIRED: ErrorKind = ErrorKind("Err-02452", 428, "Precondition Required");
109 pub const TOO_MANY_REQUESTS: ErrorKind = ErrorKind("Err-12176", 429, "Too Many Requests");
110 pub const REQUEST_HEADER_FIELDS_TOO_LARGE: ErrorKind = ErrorKind("Err-07756", 431, "Request Header Fields Too Large");
111 pub const UNAVAILABLE_FOR_LEGAL_REASONS: ErrorKind = ErrorKind("Err-12136", 451, "Unavailable For Legal Reasons");
112 pub const INTERNAL_SERVER_ERROR: ErrorKind = ErrorKind("Err-09069", 500, "Internal Server Error");
113 pub const NOT_IMPLEMENTED: ErrorKind = ErrorKind("Err-03394", 501, "Not Implemented");
114 pub const BAD_GATEWAY: ErrorKind = ErrorKind("Err-19734", 502, "Bad Gateway");
115 pub const SERVICE_UNAVAILABLE: ErrorKind = ErrorKind("Err-18979", 503, "Service Unavailable");
116 pub const GATEWAY_TIMEOUT: ErrorKind = ErrorKind("Err-17595", 504, "Gateway Timeout");
117 pub const HTTP_VERSION_NOT_SUPPORTED: ErrorKind = ErrorKind("Err-01625", 505, "HTTP Version Not Supported");
118 pub const VARIANT_ALSO_NEGOTIATES: ErrorKind = ErrorKind("Err-28382", 506, "Variant Also Negotiates");
119 pub const INSUFFICIENT_STORAGE: ErrorKind = ErrorKind("Err-32132", 507, "Insufficient Storage");
120 pub const LOOP_DETECTED: ErrorKind = ErrorKind("Err-30770", 508, "Loop Detected");
121 pub const NOT_EXTENDED: ErrorKind = ErrorKind("Err-19347", 510, "Not Extended");
122 pub const NETWORK_AUTHENTICATION_REQUIRED: ErrorKind = ErrorKind("Err-31948", 511, "Network Authentication Required");
123}
124
125impl HttpStatusCodeErrors {
126 pub fn from_status(status: http::StatusCode) -> ErrorKind {
127 match status {
128 http::StatusCode::MOVED_PERMANENTLY => HttpStatusCodeErrors::MOVED_PERMANENTLY,
129 http::StatusCode::FOUND => HttpStatusCodeErrors::FOUND,
130 http::StatusCode::SEE_OTHER => HttpStatusCodeErrors::SEE_OTHER,
131 http::StatusCode::NOT_MODIFIED => HttpStatusCodeErrors::NOT_MODIFIED,
132 http::StatusCode::USE_PROXY => HttpStatusCodeErrors::USE_PROXY,
133 http::StatusCode::TEMPORARY_REDIRECT => HttpStatusCodeErrors::TEMPORARY_REDIRECT,
134 http::StatusCode::PERMANENT_REDIRECT => HttpStatusCodeErrors::PERMANENT_REDIRECT,
135 http::StatusCode::BAD_REQUEST => HttpStatusCodeErrors::BAD_REQUEST,
136 http::StatusCode::UNAUTHORIZED => HttpStatusCodeErrors::UNAUTHORIZED,
137 http::StatusCode::PAYMENT_REQUIRED => HttpStatusCodeErrors::PAYMENT_REQUIRED,
138 http::StatusCode::FORBIDDEN => HttpStatusCodeErrors::FORBIDDEN,
139 http::StatusCode::NOT_FOUND => HttpStatusCodeErrors::NOT_FOUND,
140 http::StatusCode::METHOD_NOT_ALLOWED => HttpStatusCodeErrors::METHOD_NOT_ALLOWED,
141 http::StatusCode::NOT_ACCEPTABLE => HttpStatusCodeErrors::NOT_ACCEPTABLE,
142 http::StatusCode::PROXY_AUTHENTICATION_REQUIRED => HttpStatusCodeErrors::PROXY_AUTHENTICATION_REQUIRED,
143 http::StatusCode::REQUEST_TIMEOUT => HttpStatusCodeErrors::REQUEST_TIMEOUT,
144 http::StatusCode::CONFLICT => HttpStatusCodeErrors::CONFLICT,
145 http::StatusCode::GONE => HttpStatusCodeErrors::GONE,
146 http::StatusCode::LENGTH_REQUIRED => HttpStatusCodeErrors::LENGTH_REQUIRED,
147 http::StatusCode::PRECONDITION_FAILED => HttpStatusCodeErrors::PRECONDITION_FAILED,
148 http::StatusCode::PAYLOAD_TOO_LARGE => HttpStatusCodeErrors::PAYLOAD_TOO_LARGE,
149 http::StatusCode::URI_TOO_LONG => HttpStatusCodeErrors::URI_TOO_LONG,
150 http::StatusCode::UNSUPPORTED_MEDIA_TYPE => HttpStatusCodeErrors::UNSUPPORTED_MEDIA_TYPE,
151 http::StatusCode::RANGE_NOT_SATISFIABLE => HttpStatusCodeErrors::RANGE_NOT_SATISFIABLE,
152 http::StatusCode::EXPECTATION_FAILED => HttpStatusCodeErrors::EXPECTATION_FAILED,
153 http::StatusCode::IM_A_TEAPOT => HttpStatusCodeErrors::IM_A_TEAPOT,
154 http::StatusCode::MISDIRECTED_REQUEST => HttpStatusCodeErrors::MISDIRECTED_REQUEST,
155 http::StatusCode::UNPROCESSABLE_ENTITY => HttpStatusCodeErrors::UNPROCESSABLE_ENTITY,
156 http::StatusCode::LOCKED => HttpStatusCodeErrors::LOCKED,
157 http::StatusCode::FAILED_DEPENDENCY => HttpStatusCodeErrors::FAILED_DEPENDENCY,
158 http::StatusCode::UPGRADE_REQUIRED => HttpStatusCodeErrors::UPGRADE_REQUIRED,
159 http::StatusCode::PRECONDITION_REQUIRED => HttpStatusCodeErrors::PRECONDITION_REQUIRED,
160 http::StatusCode::TOO_MANY_REQUESTS => HttpStatusCodeErrors::TOO_MANY_REQUESTS,
161 http::StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE => HttpStatusCodeErrors::REQUEST_HEADER_FIELDS_TOO_LARGE,
162 http::StatusCode::UNAVAILABLE_FOR_LEGAL_REASONS => HttpStatusCodeErrors::UNAVAILABLE_FOR_LEGAL_REASONS,
163 http::StatusCode::INTERNAL_SERVER_ERROR => HttpStatusCodeErrors::INTERNAL_SERVER_ERROR,
164 http::StatusCode::NOT_IMPLEMENTED => HttpStatusCodeErrors::NOT_IMPLEMENTED,
165 http::StatusCode::BAD_GATEWAY => HttpStatusCodeErrors::BAD_GATEWAY,
166 http::StatusCode::SERVICE_UNAVAILABLE => HttpStatusCodeErrors::SERVICE_UNAVAILABLE,
167 http::StatusCode::GATEWAY_TIMEOUT => HttpStatusCodeErrors::GATEWAY_TIMEOUT,
168 http::StatusCode::HTTP_VERSION_NOT_SUPPORTED => HttpStatusCodeErrors::HTTP_VERSION_NOT_SUPPORTED,
169 http::StatusCode::VARIANT_ALSO_NEGOTIATES => HttpStatusCodeErrors::VARIANT_ALSO_NEGOTIATES,
170 http::StatusCode::INSUFFICIENT_STORAGE => HttpStatusCodeErrors::INSUFFICIENT_STORAGE,
171 http::StatusCode::LOOP_DETECTED => HttpStatusCodeErrors::LOOP_DETECTED,
172 http::StatusCode::NOT_EXTENDED => HttpStatusCodeErrors::NOT_EXTENDED,
173 http::StatusCode::NETWORK_AUTHENTICATION_REQUIRED => HttpStatusCodeErrors::NETWORK_AUTHENTICATION_REQUIRED,
174 _ => panic!("{} is not an error!", status),
175 }
176 }
177}