1use std::fmt;
2
3use crate::context::{
4 Aborted, AlreadyExists, Cancelled, DataLoss, DeadlineExceeded, FailedPrecondition, Internal,
5 InvalidArgument, NotFound, OutOfRange, PermissionDenied, ResourceExhausted, ServiceUnavailable,
6 Unauthenticated, Unimplemented, Unknown,
7};
8
9#[derive(Debug, Clone)]
14#[non_exhaustive]
15pub enum CanonicalError {
16 #[non_exhaustive]
17 Cancelled {
18 ctx: Cancelled,
19 detail: String,
20 resource_type: Option<String>,
21 resource_name: Option<String>,
22 },
23 #[non_exhaustive]
24 Unknown {
25 ctx: Unknown,
26 detail: String,
27 resource_type: Option<String>,
28 resource_name: Option<String>,
29 },
30 #[non_exhaustive]
31 InvalidArgument {
32 ctx: InvalidArgument,
33 detail: String,
34 resource_type: Option<String>,
35 resource_name: Option<String>,
36 },
37 #[non_exhaustive]
38 DeadlineExceeded {
39 ctx: DeadlineExceeded,
40 detail: String,
41 resource_type: Option<String>,
42 resource_name: Option<String>,
43 },
44 #[non_exhaustive]
45 NotFound {
46 ctx: NotFound,
47 detail: String,
48 resource_type: Option<String>,
49 resource_name: Option<String>,
50 },
51 #[non_exhaustive]
52 AlreadyExists {
53 ctx: AlreadyExists,
54 detail: String,
55 resource_type: Option<String>,
56 resource_name: Option<String>,
57 },
58 #[non_exhaustive]
59 PermissionDenied {
60 ctx: PermissionDenied,
61 detail: String,
62 resource_type: Option<String>,
63 resource_name: Option<String>,
64 },
65 #[non_exhaustive]
66 ResourceExhausted {
67 ctx: ResourceExhausted,
68 detail: String,
69 resource_type: Option<String>,
70 resource_name: Option<String>,
71 },
72 #[non_exhaustive]
73 FailedPrecondition {
74 ctx: FailedPrecondition,
75 detail: String,
76 resource_type: Option<String>,
77 resource_name: Option<String>,
78 },
79 #[non_exhaustive]
80 Aborted {
81 ctx: Aborted,
82 detail: String,
83 resource_type: Option<String>,
84 resource_name: Option<String>,
85 },
86 #[non_exhaustive]
87 OutOfRange {
88 ctx: OutOfRange,
89 detail: String,
90 resource_type: Option<String>,
91 resource_name: Option<String>,
92 },
93 #[non_exhaustive]
94 Unimplemented {
95 ctx: Unimplemented,
96 detail: String,
97 resource_type: Option<String>,
98 resource_name: Option<String>,
99 },
100 #[non_exhaustive]
101 Internal { ctx: Internal, detail: String },
102 #[non_exhaustive]
103 ServiceUnavailable {
104 ctx: ServiceUnavailable,
105 detail: String,
106 resource_type: Option<String>,
107 resource_name: Option<String>,
108 },
109 #[non_exhaustive]
110 DataLoss {
111 ctx: DataLoss,
112 detail: String,
113 resource_type: Option<String>,
114 resource_name: Option<String>,
115 },
116 #[non_exhaustive]
117 Unauthenticated {
118 ctx: Unauthenticated,
119 detail: String,
120 resource_type: Option<String>,
121 resource_name: Option<String>,
122 },
123}
124
125impl CanonicalError {
126 #[doc(hidden)]
129 #[must_use]
130 pub(crate) fn __cancelled(ctx: Cancelled) -> Self {
131 Self::Cancelled {
132 ctx,
133 detail: String::from("Operation cancelled by the client"),
134 resource_type: None,
135 resource_name: None,
136 }
137 }
138
139 #[doc(hidden)]
140 #[must_use]
141 pub(crate) fn __unknown(ctx: Unknown) -> Self {
142 Self::Unknown {
143 ctx,
144 detail: String::from("An unknown error occurred"),
145 resource_type: None,
146 resource_name: None,
147 }
148 }
149
150 #[doc(hidden)]
151 #[must_use]
152 pub(crate) fn __invalid_argument(ctx: InvalidArgument) -> Self {
153 let detail = match &ctx {
154 InvalidArgument::FieldViolations { .. } => String::from("Request validation failed"),
155 InvalidArgument::Format { format } => format.clone(),
156 InvalidArgument::Constraint { constraint } => constraint.clone(),
157 };
158 Self::InvalidArgument {
159 ctx,
160 detail,
161 resource_type: None,
162 resource_name: None,
163 }
164 }
165
166 #[doc(hidden)]
167 #[must_use]
168 pub(crate) fn __deadline_exceeded(ctx: DeadlineExceeded) -> Self {
169 Self::DeadlineExceeded {
170 ctx,
171 detail: String::from("Operation did not complete within the allowed time"),
172 resource_type: None,
173 resource_name: None,
174 }
175 }
176
177 #[doc(hidden)]
178 #[must_use]
179 pub(crate) fn __not_found(ctx: NotFound) -> Self {
180 Self::NotFound {
181 ctx,
182 detail: String::from("Resource not found"),
183 resource_type: None,
184 resource_name: None,
185 }
186 }
187
188 #[doc(hidden)]
189 #[must_use]
190 pub(crate) fn __already_exists(ctx: AlreadyExists) -> Self {
191 Self::AlreadyExists {
192 ctx,
193 detail: String::from("Resource already exists"),
194 resource_type: None,
195 resource_name: None,
196 }
197 }
198
199 #[doc(hidden)]
200 #[must_use]
201 pub(crate) fn __permission_denied(ctx: PermissionDenied) -> Self {
202 Self::PermissionDenied {
203 ctx,
204 detail: String::from("You do not have permission to perform this operation"),
205 resource_type: None,
206 resource_name: None,
207 }
208 }
209
210 #[doc(hidden)]
211 #[must_use]
212 pub(crate) fn __resource_exhausted(ctx: ResourceExhausted) -> Self {
213 Self::ResourceExhausted {
214 ctx,
215 detail: String::from("Quota exceeded"),
216 resource_type: None,
217 resource_name: None,
218 }
219 }
220
221 #[doc(hidden)]
222 #[must_use]
223 pub(crate) fn __failed_precondition(ctx: FailedPrecondition) -> Self {
224 Self::FailedPrecondition {
225 ctx,
226 detail: String::from("Operation precondition not met"),
227 resource_type: None,
228 resource_name: None,
229 }
230 }
231
232 #[doc(hidden)]
233 #[must_use]
234 pub(crate) fn __aborted(ctx: Aborted) -> Self {
235 Self::Aborted {
236 ctx,
237 detail: String::from("Operation aborted due to concurrency conflict"),
238 resource_type: None,
239 resource_name: None,
240 }
241 }
242
243 #[doc(hidden)]
244 #[must_use]
245 pub(crate) fn __out_of_range(ctx: OutOfRange) -> Self {
246 Self::OutOfRange {
247 ctx,
248 detail: String::from("Value out of range"),
249 resource_type: None,
250 resource_name: None,
251 }
252 }
253
254 #[doc(hidden)]
255 #[must_use]
256 pub(crate) fn __unimplemented(ctx: Unimplemented) -> Self {
257 Self::Unimplemented {
258 ctx,
259 detail: String::from("This operation is not implemented"),
260 resource_type: None,
261 resource_name: None,
262 }
263 }
264
265 #[doc(hidden)]
266 #[must_use]
267 pub(crate) fn __internal(ctx: Internal) -> Self {
268 Self::Internal {
269 ctx,
270 detail: String::from("An internal error occurred. Please retry later."),
271 }
272 }
273
274 #[doc(hidden)]
275 #[must_use]
276 pub(crate) fn __service_unavailable(ctx: ServiceUnavailable) -> Self {
277 Self::ServiceUnavailable {
278 ctx,
279 detail: String::from("Service temporarily unavailable"),
280 resource_type: None,
281 resource_name: None,
282 }
283 }
284
285 #[doc(hidden)]
286 #[must_use]
287 pub(crate) fn __data_loss(ctx: DataLoss) -> Self {
288 Self::DataLoss {
289 ctx,
290 detail: String::from("Data loss detected"),
291 resource_type: None,
292 resource_name: None,
293 }
294 }
295
296 #[doc(hidden)]
297 #[must_use]
298 pub(crate) fn __unauthenticated(ctx: Unauthenticated) -> Self {
299 Self::Unauthenticated {
300 ctx,
301 detail: String::from("Authentication required"),
302 resource_type: None,
303 resource_name: None,
304 }
305 }
306
307 #[must_use]
310 pub(crate) fn with_detail(mut self, msg: impl Into<String>) -> Self {
311 let msg = msg.into();
312 match &mut self {
313 Self::Cancelled { detail, .. }
314 | Self::Unknown { detail, .. }
315 | Self::InvalidArgument { detail, .. }
316 | Self::DeadlineExceeded { detail, .. }
317 | Self::NotFound { detail, .. }
318 | Self::AlreadyExists { detail, .. }
319 | Self::PermissionDenied { detail, .. }
320 | Self::ResourceExhausted { detail, .. }
321 | Self::FailedPrecondition { detail, .. }
322 | Self::Aborted { detail, .. }
323 | Self::OutOfRange { detail, .. }
324 | Self::Unimplemented { detail, .. }
325 | Self::ServiceUnavailable { detail, .. }
326 | Self::DataLoss { detail, .. }
327 | Self::Unauthenticated { detail, .. }
328 | Self::Internal { detail, .. } => *detail = msg,
329 }
330 self
331 }
332
333 #[must_use]
334 pub(crate) fn with_resource_type(mut self, rt: impl Into<String>) -> Self {
335 let rt = Some(rt.into());
336 match &mut self {
337 Self::Cancelled { resource_type, .. }
338 | Self::Unknown { resource_type, .. }
339 | Self::InvalidArgument { resource_type, .. }
340 | Self::DeadlineExceeded { resource_type, .. }
341 | Self::NotFound { resource_type, .. }
342 | Self::AlreadyExists { resource_type, .. }
343 | Self::PermissionDenied { resource_type, .. }
344 | Self::ResourceExhausted { resource_type, .. }
345 | Self::FailedPrecondition { resource_type, .. }
346 | Self::Aborted { resource_type, .. }
347 | Self::OutOfRange { resource_type, .. }
348 | Self::Unimplemented { resource_type, .. }
349 | Self::ServiceUnavailable { resource_type, .. }
350 | Self::DataLoss { resource_type, .. }
351 | Self::Unauthenticated { resource_type, .. } => *resource_type = rt,
352 Self::Internal { .. } => {}
353 }
354 self
355 }
356
357 #[must_use]
358 pub(crate) fn with_resource(mut self, rn: impl Into<String>) -> Self {
359 let rn = Some(rn.into());
360 match &mut self {
361 Self::Cancelled { resource_name, .. }
362 | Self::Unknown { resource_name, .. }
363 | Self::InvalidArgument { resource_name, .. }
364 | Self::DeadlineExceeded { resource_name, .. }
365 | Self::NotFound { resource_name, .. }
366 | Self::AlreadyExists { resource_name, .. }
367 | Self::PermissionDenied { resource_name, .. }
368 | Self::ResourceExhausted { resource_name, .. }
369 | Self::FailedPrecondition { resource_name, .. }
370 | Self::Aborted { resource_name, .. }
371 | Self::OutOfRange { resource_name, .. }
372 | Self::Unimplemented { resource_name, .. }
373 | Self::ServiceUnavailable { resource_name, .. }
374 | Self::DataLoss { resource_name, .. }
375 | Self::Unauthenticated { resource_name, .. } => *resource_name = rn,
376 Self::Internal { .. } => {}
377 }
378 self
379 }
380
381 #[must_use]
384 pub fn detail(&self) -> &str {
385 match self {
386 Self::Cancelled { detail, .. }
387 | Self::Unknown { detail, .. }
388 | Self::InvalidArgument { detail, .. }
389 | Self::DeadlineExceeded { detail, .. }
390 | Self::NotFound { detail, .. }
391 | Self::AlreadyExists { detail, .. }
392 | Self::PermissionDenied { detail, .. }
393 | Self::ResourceExhausted { detail, .. }
394 | Self::FailedPrecondition { detail, .. }
395 | Self::Aborted { detail, .. }
396 | Self::OutOfRange { detail, .. }
397 | Self::Unimplemented { detail, .. }
398 | Self::ServiceUnavailable { detail, .. }
399 | Self::DataLoss { detail, .. }
400 | Self::Unauthenticated { detail, .. }
401 | Self::Internal { detail, .. } => detail,
402 }
403 }
404
405 #[must_use]
406 pub fn resource_type(&self) -> Option<&str> {
407 match self {
408 Self::Cancelled { resource_type, .. }
409 | Self::Unknown { resource_type, .. }
410 | Self::InvalidArgument { resource_type, .. }
411 | Self::DeadlineExceeded { resource_type, .. }
412 | Self::NotFound { resource_type, .. }
413 | Self::AlreadyExists { resource_type, .. }
414 | Self::PermissionDenied { resource_type, .. }
415 | Self::ResourceExhausted { resource_type, .. }
416 | Self::FailedPrecondition { resource_type, .. }
417 | Self::Aborted { resource_type, .. }
418 | Self::OutOfRange { resource_type, .. }
419 | Self::Unimplemented { resource_type, .. }
420 | Self::ServiceUnavailable { resource_type, .. }
421 | Self::DataLoss { resource_type, .. }
422 | Self::Unauthenticated { resource_type, .. } => resource_type.as_deref(),
423 Self::Internal { .. } => None,
424 }
425 }
426
427 #[must_use]
428 pub fn resource_name(&self) -> Option<&str> {
429 match self {
430 Self::Cancelled { resource_name, .. }
431 | Self::Unknown { resource_name, .. }
432 | Self::InvalidArgument { resource_name, .. }
433 | Self::DeadlineExceeded { resource_name, .. }
434 | Self::NotFound { resource_name, .. }
435 | Self::AlreadyExists { resource_name, .. }
436 | Self::PermissionDenied { resource_name, .. }
437 | Self::ResourceExhausted { resource_name, .. }
438 | Self::FailedPrecondition { resource_name, .. }
439 | Self::Aborted { resource_name, .. }
440 | Self::OutOfRange { resource_name, .. }
441 | Self::Unimplemented { resource_name, .. }
442 | Self::ServiceUnavailable { resource_name, .. }
443 | Self::DataLoss { resource_name, .. }
444 | Self::Unauthenticated { resource_name, .. } => resource_name.as_deref(),
445 Self::Internal { .. } => None,
446 }
447 }
448
449 #[must_use]
456 pub fn diagnostic(&self) -> Option<&str> {
457 match self {
458 Self::Internal { ctx, .. } => Some(&ctx.description),
459 Self::Unknown { ctx, .. } => Some(&ctx.description),
460 _ => None,
461 }
462 }
463
464 #[must_use]
467 pub fn gts_type(&self) -> &'static str {
468 match self {
469 Self::Cancelled { .. } => "gts.cf.core.errors.err.v1~cf.core.err.cancelled.v1~",
470 Self::Unknown { .. } => "gts.cf.core.errors.err.v1~cf.core.err.unknown.v1~",
471 Self::InvalidArgument { .. } => {
472 "gts.cf.core.errors.err.v1~cf.core.err.invalid_argument.v1~"
473 }
474 Self::DeadlineExceeded { .. } => {
475 "gts.cf.core.errors.err.v1~cf.core.err.deadline_exceeded.v1~"
476 }
477 Self::NotFound { .. } => "gts.cf.core.errors.err.v1~cf.core.err.not_found.v1~",
478 Self::AlreadyExists { .. } => {
479 "gts.cf.core.errors.err.v1~cf.core.err.already_exists.v1~"
480 }
481 Self::PermissionDenied { .. } => {
482 "gts.cf.core.errors.err.v1~cf.core.err.permission_denied.v1~"
483 }
484 Self::ResourceExhausted { .. } => {
485 "gts.cf.core.errors.err.v1~cf.core.err.resource_exhausted.v1~"
486 }
487 Self::FailedPrecondition { .. } => {
488 "gts.cf.core.errors.err.v1~cf.core.err.failed_precondition.v1~"
489 }
490 Self::Aborted { .. } => "gts.cf.core.errors.err.v1~cf.core.err.aborted.v1~",
491 Self::OutOfRange { .. } => "gts.cf.core.errors.err.v1~cf.core.err.out_of_range.v1~",
492 Self::Unimplemented { .. } => "gts.cf.core.errors.err.v1~cf.core.err.unimplemented.v1~",
493 Self::Internal { .. } => "gts.cf.core.errors.err.v1~cf.core.err.internal.v1~",
494 Self::ServiceUnavailable { .. } => {
495 "gts.cf.core.errors.err.v1~cf.core.err.service_unavailable.v1~"
496 }
497 Self::DataLoss { .. } => "gts.cf.core.errors.err.v1~cf.core.err.data_loss.v1~",
498 Self::Unauthenticated { .. } => {
499 "gts.cf.core.errors.err.v1~cf.core.err.unauthenticated.v1~"
500 }
501 }
502 }
503
504 #[must_use]
505 pub fn status_code(&self) -> u16 {
506 match self {
507 Self::InvalidArgument { .. }
508 | Self::FailedPrecondition { .. }
509 | Self::OutOfRange { .. } => 400,
510 Self::Unauthenticated { .. } => 401,
511 Self::PermissionDenied { .. } => 403,
512 Self::NotFound { .. } => 404,
513 Self::AlreadyExists { .. } | Self::Aborted { .. } => 409,
514 Self::ResourceExhausted { .. } => 429,
515 Self::Cancelled { .. } => 499,
516 Self::Unknown { .. } | Self::Internal { .. } | Self::DataLoss { .. } => 500,
517 Self::Unimplemented { .. } => 501,
518 Self::ServiceUnavailable { .. } => 503,
519 Self::DeadlineExceeded { .. } => 504,
520 }
521 }
522
523 #[must_use]
524 pub fn title(&self) -> &'static str {
525 match self {
526 Self::Cancelled { .. } => "Cancelled",
527 Self::Unknown { .. } => "Unknown",
528 Self::InvalidArgument { .. } => "Invalid Argument",
529 Self::DeadlineExceeded { .. } => "Deadline Exceeded",
530 Self::NotFound { .. } => "Not Found",
531 Self::AlreadyExists { .. } => "Already Exists",
532 Self::PermissionDenied { .. } => "Permission Denied",
533 Self::ResourceExhausted { .. } => "Resource Exhausted",
534 Self::FailedPrecondition { .. } => "Failed Precondition",
535 Self::Aborted { .. } => "Aborted",
536 Self::OutOfRange { .. } => "Out of Range",
537 Self::Unimplemented { .. } => "Unimplemented",
538 Self::Internal { .. } => "Internal",
539 Self::ServiceUnavailable { .. } => "Service Unavailable",
540 Self::DataLoss { .. } => "Data Loss",
541 Self::Unauthenticated { .. } => "Unauthenticated",
542 }
543 }
544
545 fn category_name(&self) -> &'static str {
546 match self {
547 Self::Cancelled { .. } => "cancelled",
548 Self::Unknown { .. } => "unknown",
549 Self::InvalidArgument { .. } => "invalid_argument",
550 Self::DeadlineExceeded { .. } => "deadline_exceeded",
551 Self::NotFound { .. } => "not_found",
552 Self::AlreadyExists { .. } => "already_exists",
553 Self::PermissionDenied { .. } => "permission_denied",
554 Self::ResourceExhausted { .. } => "resource_exhausted",
555 Self::FailedPrecondition { .. } => "failed_precondition",
556 Self::Aborted { .. } => "aborted",
557 Self::OutOfRange { .. } => "out_of_range",
558 Self::Unimplemented { .. } => "unimplemented",
559 Self::Internal { .. } => "internal",
560 Self::ServiceUnavailable { .. } => "service_unavailable",
561 Self::DataLoss { .. } => "data_loss",
562 Self::Unauthenticated { .. } => "unauthenticated",
563 }
564 }
565}
566
567impl fmt::Display for CanonicalError {
568 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
569 write!(f, "{}: {}", self.category_name(), self.detail())
570 }
571}
572
573impl std::error::Error for CanonicalError {}
574
575impl From<std::io::Error> for CanonicalError {
580 fn from(err: std::io::Error) -> Self {
581 Self::__internal(Internal::new(err.to_string()))
582 }
583}
584
585impl From<serde_json::Error> for CanonicalError {
586 fn from(err: serde_json::Error) -> Self {
587 Self::__internal(Internal::new(err.to_string())).with_detail("Malformed JSON request body")
588 }
589}
590
591#[cfg(feature = "sea-orm")]
592impl From<sea_orm::DbErr> for CanonicalError {
593 fn from(err: sea_orm::DbErr) -> Self {
594 Self::__internal(Internal::new(err.to_string()))
595 }
596}