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