1use std::any::Any;
2use std::borrow::Cow;
3use std::fmt::{Debug, Display, Formatter};
4use std::sync::Arc;
5use indexmap::{IndexMap, indexmap};
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8
9pub struct Error {
10 pub code: u16,
11 pub message: String,
12 pub errors: Option<IndexMap<String, String>>,
13 pub platform_native_object: Option<Arc<dyn Any + Send + Sync>>,
14}
15
16#[derive(Serialize, Deserialize)]
17pub struct ErrorSerializable {
18 pub code: u16,
19 pub message: String,
20 pub errors: Value,
21}
22
23impl ErrorSerializable {
24 pub fn from_error(error: &Error) -> Self {
25 ErrorSerializable {
26 code: error.code,
27 message: error.message().to_string(),
28 errors: if let Some(errors) = error.errors() {
29 Value::Object(errors.iter().map(|(k, v)| (k.to_string(), Value::String(v.to_string()))).collect())
30 } else {
31 Value::Null
32 },
33 }
34 }
35
36 pub fn error_string(error: &Error) -> String {
37 let serializable = Self::from_error(error);
38 serde_json::to_string(&serializable).unwrap()
39 }
40}
41
42impl Error {
43
44 pub fn new(message: impl Into<String>) -> Self {
45 Self {
46 code: 500,
47 message: message.into(),
48 errors: None,
49 platform_native_object: None,
50 }
51 }
52
53 pub fn new_with_code(message: impl Into<String>, code: u16) -> Self {
54 Self {
55 code,
56 message: message.into(),
57 errors: None,
58 platform_native_object: None,
59 }
60 }
61
62 pub fn new_with_code_errors(message: impl Into<String>, code: u16, errors: IndexMap<String, String>) -> Self {
63 Self {
64 code,
65 message: message.into(),
66 errors: Some(errors),
67 platform_native_object: None,
68 }
69 }
70
71 pub fn new_pathed(message: impl Into<String>, code: u16, key: impl Into<String>, value: impl Into<String>) -> Self {
72 Self {
73 code,
74 message: message.into(),
75 errors: Some(indexmap! { key.into() => value.into() }),
76 platform_native_object: None,
77 }
78 }
79
80 pub fn message_prefixed(&self, prefix: impl AsRef<str>) -> Self {
81 Self {
82 code: self.code,
83 message: if self.errors.is_some() {
84 self.message.clone()
85 } else {
86 format!("{}: {}", prefix.as_ref(), self.message())
87 },
88 errors: if let Some(errors) = self.errors.as_ref() {
89 Some(errors.iter().map(|(k, v)| (k.clone(), format!("{}: {}", prefix.as_ref(), v))).collect())
90 } else {
91 None
92 },
93 platform_native_object: self.platform_native_object.clone(),
94 }
95 }
96
97 pub fn path_prefixed(&self, prefix: impl AsRef<str>) -> Self {
98 Self {
99 code: self.code,
100 message: self.message.clone(),
101 errors: if let Some(errors) = self.errors.as_ref() {
102 Some(errors.iter().map(|(k, v)| (format!("{}.{}", prefix.as_ref(), k), v.clone())).collect())
103 } else {
104 None
105 },
106 platform_native_object: self.platform_native_object.clone(),
107 }
108 }
109
110 pub fn pathed(&self, prefix: impl AsRef<str>) -> Self {
111 Self {
112 code: self.code,
113 message: self.message.clone(),
114 errors: if let Some(errors) = self.errors.as_ref() {
115 Some(errors.iter().map(|(k, v)| (k.to_owned(), v.clone())).collect())
116 } else {
117 Some(indexmap! {prefix.as_ref().to_string() => self.message.clone()})
118 },
119 platform_native_object: self.platform_native_object.clone(),
120 }
121 }
122
123 pub fn map_path<F>(&self, mapper: F) -> Self where F: Fn(&str) -> String {
124 Self {
125 code: self.code,
126 message: self.message.clone(),
127 errors: if let Some(errors) = self.errors.as_ref() {
128 Some(errors.iter().map(|(k, v)| (mapper(k.as_str()), v.clone())).collect())
129 } else {
130 None
131 },
132 platform_native_object: self.platform_native_object.clone(),
133 }
134 }
135
136 pub fn code(&self) -> u16 {
137 self.code
138 }
139
140 pub fn message(&self) -> &str {
141 self.message.as_str()
142 }
143
144 pub fn errors(&self) -> Option<&IndexMap<String, String>> {
145 self.errors.as_ref()
146 }
147
148 pub fn assign_platform_native_object<T: 'static + Send + Sync>(&mut self, val: T) {
149 self.platform_native_object = Some(Arc::new(val));
150 }
151
152 pub fn platform_native_object<T: 'static + Send>(&self) -> Option<&T> {
153 self.platform_native_object.as_ref().map(|boxed| boxed.downcast_ref()).flatten()
154 }
155
156 pub fn inferred_title(&self) -> Cow<'static, str> {
157 match self.code {
158 100 => Cow::Borrowed("Continue"),
159 101 => Cow::Borrowed("SwitchingProtocols"),
160 102 => Cow::Borrowed("Processing"),
161 103 => Cow::Borrowed("EarlyHints"),
162 200 => Cow::Borrowed("OK"),
163 201 => Cow::Borrowed("Created"),
164 202 => Cow::Borrowed("Accepted"),
165 203 => Cow::Borrowed("NonAuthoritativeInformation"),
166 204 => Cow::Borrowed("NoContent"),
167 205 => Cow::Borrowed("ResetContent"),
168 206 => Cow::Borrowed("PartialContent"),
169 207 => Cow::Borrowed("MultiStatus"),
170 208 => Cow::Borrowed("AlreadyReported"),
171 226 => Cow::Borrowed("IMUsed"),
172 300 => Cow::Borrowed("MultipleChoices"),
173 301 => Cow::Borrowed("MovedPermanently"),
174 302 => Cow::Borrowed("Found"),
175 303 => Cow::Borrowed("SeeOther"),
176 304 => Cow::Borrowed("NotModified"),
177 307 => Cow::Borrowed("TemporaryRedirect"),
178 308 => Cow::Borrowed("PermanentRedirect"),
179 400 => Cow::Borrowed("BadRequest"),
180 401 => Cow::Borrowed("Unauthorized"),
181 402 => Cow::Borrowed("PaymentRequired"),
182 403 => Cow::Borrowed("Forbidden"),
183 404 => Cow::Borrowed("NotFound"),
184 405 => Cow::Borrowed("MethodNotAllowed"),
185 406 => Cow::Borrowed("NotAcceptable"),
186 407 => Cow::Borrowed("ProxyAuthenticationRequired"),
187 408 => Cow::Borrowed("RequestTimeout"),
188 409 => Cow::Borrowed("Conflict"),
189 410 => Cow::Borrowed("Gone"),
190 411 => Cow::Borrowed("LengthRequired"),
191 412 => Cow::Borrowed("PreconditionFailed"),
192 413 => Cow::Borrowed("PayloadTooLarge"),
193 414 => Cow::Borrowed("URITooLong"),
194 415 => Cow::Borrowed("UnsupportedMediaType"),
195 416 => Cow::Borrowed("RangeNotSatisfiable"),
196 417 => Cow::Borrowed("ExpectationFailed"),
197 418 => Cow::Borrowed("ImATeapot"),
198 421 => Cow::Borrowed("MisdirectedRequest"),
199 422 => Cow::Borrowed("UnprocessableContent"),
200 423 => Cow::Borrowed("Locked"),
201 424 => Cow::Borrowed("FailedDependency"),
202 425 => Cow::Borrowed("TooEarly"),
203 426 => Cow::Borrowed("UpgradeRequired"),
204 428 => Cow::Borrowed("PreconditionRequired"),
205 429 => Cow::Borrowed("TooManyRequests"),
206 431 => Cow::Borrowed("RequestHeaderFieldsTooLarge"),
207 451 => Cow::Borrowed("UnavailableForLegalReasons"),
208 500 => Cow::Borrowed("InternalServerError"),
209 501 => Cow::Borrowed("NotImplemented"),
210 502 => Cow::Borrowed("BadGateway"),
211 503 => Cow::Borrowed("ServiceUnavailable"),
212 504 => Cow::Borrowed("GatewayTimeout"),
213 505 => Cow::Borrowed("HTTPVersionNotSupported"),
214 506 => Cow::Borrowed("VariantAlsoNegotiates"),
215 507 => Cow::Borrowed("InsufficientStorage"),
216 508 => Cow::Borrowed("LoopDetected"),
217 510 => Cow::Borrowed("NotExtended"),
218 511 => Cow::Borrowed("NetworkAuthenticationRequired"),
219 _ => Cow::Owned(format!("ServerError({})", self.code)),
220 }
221 }
222
223 pub fn not_found() -> Self {
224 Self {
225 code: 404,
226 message: "not found".to_owned(),
227 errors: None,
228 platform_native_object: None,
229 }
230 }
231
232 pub fn not_found_message(message: impl Into<String>) -> Self {
233 Self {
234 code: 404,
235 message: message.into(),
236 errors: None,
237 platform_native_object: None,
238 }
239 }
240
241 pub fn not_found_pathed(path: impl Into<String>, message: impl Into<String>) -> Self {
242 Self {
243 code: 404,
244 message: "not found".to_owned(),
245 errors: Some(indexmap! {
246 path.into() => message.into()
247 }),
248 platform_native_object: None,
249 }
250 }
251
252 pub fn invalid_request() -> Self {
253 Self {
254 code: 400,
255 message: "value is invalid".to_owned(),
256 errors: None,
257 platform_native_object: None,
258 }
259 }
260
261 pub fn invalid_request_message(message: impl Into<String>) -> Self {
262 Self {
263 code: 400,
264 message: message.into(),
265 errors: None,
266 platform_native_object: None,
267 }
268 }
269
270 pub fn invalid_request_pathed(path: impl Into<String>, message: impl Into<String>) -> Self {
271 Self {
272 code: 400,
273 message: "value is invalid".to_owned(),
274 errors: Some(indexmap! {
275 path.into() => message.into()
276 }),
277 platform_native_object: None,
278 }
279 }
280
281 pub fn unique_error(path: impl Into<String>, constraint: impl AsRef<str>) -> Self {
282 Self {
283 code: 400,
284 message: "value is invalid".to_owned(),
285 errors: Some(indexmap! {
286 path.into() => format!("value violates '{}' constraint", constraint.as_ref())
287 }),
288 platform_native_object: None,
289 }
290 }
291
292 pub fn internal_server_error() -> Self {
293 Self {
294 code: 500,
295 message: "internal server error".to_owned(),
296 errors: None,
297 platform_native_object: None,
298 }
299 }
300
301 pub fn internal_server_error_message(message: impl Into<String>) -> Self {
302 Self {
303 code: 500,
304 message: message.into(),
305 errors: None,
306 platform_native_object: None,
307 }
308 }
309
310 pub fn internal_server_error_pathed(path: impl Into<String>, message: impl Into<String>) -> Self {
311 Self {
312 code: 500,
313 message: "internal server error".to_owned(),
314 errors: Some(indexmap! {
315 path.into() => message.into()
316 }),
317 platform_native_object: None,
318 }
319 }
320
321 pub fn unauthorized() -> Self {
322 Self {
323 code: 401,
324 message: "unauthorized".to_owned(),
325 errors: None,
326 platform_native_object: None,
327 }
328 }
329
330 pub fn unauthorized_message(message: impl Into<String>) -> Self {
331 Self {
332 code: 401,
333 message: message.into(),
334 errors: None,
335 platform_native_object: None,
336 }
337 }
338
339 pub fn unauthorized_pathed(path: impl Into<String>, message: impl Into<String>) -> Self {
340 Self {
341 code: 401,
342 message: "unauthorized".to_owned(),
343 errors: Some(indexmap! {
344 path.into() => message.into()
345 }),
346 platform_native_object: None,
347 }
348 }
349}
350
351impl Debug for Error {
352 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
353 let serialized = ErrorSerializable::error_string(self);
354 f.write_str(&format!("teo_result::Error: {}", serialized))
355 }
356}
357
358impl Display for Error {
359 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
360 Debug::fmt(self, f)
361 }
362}
363
364impl std::error::Error for Error { }