1 use prost::{
2 bytes::{Bytes, BytesMut},
3 DecodeError, Message,
4 };
5 use prost_types::Any;
6 use tonic::{metadata::MetadataMap, Code};
7
8 mod error_details;
9 mod std_messages;
10
11 use super::pb;
12
13 pub use error_details::{vec::ErrorDetail, ErrorDetails};
14 pub use std_messages::{
15 BadRequest, DebugInfo, ErrorInfo, FieldViolation, Help, HelpLink, LocalizedMessage,
16 PreconditionFailure, PreconditionViolation, QuotaFailure, QuotaViolation, RequestInfo,
17 ResourceInfo, RetryInfo,
18 };
19
20 trait IntoAny {
into_any(self) -> Any21 fn into_any(self) -> Any;
22 }
23
24 #[allow(dead_code)]
25 trait FromAny {
from_any(any: Any) -> Result<Self, DecodeError> where Self: Sized26 fn from_any(any: Any) -> Result<Self, DecodeError>
27 where
28 Self: Sized;
29 }
30
31 trait FromAnyRef {
from_any_ref(any: &Any) -> Result<Self, DecodeError> where Self: Sized32 fn from_any_ref(any: &Any) -> Result<Self, DecodeError>
33 where
34 Self: Sized;
35 }
36
gen_details_bytes(code: Code, message: &str, details: Vec<Any>) -> Bytes37 fn gen_details_bytes(code: Code, message: &str, details: Vec<Any>) -> Bytes {
38 let status = pb::Status {
39 code: code as i32,
40 message: message.to_owned(),
41 details,
42 };
43
44 let mut buf = BytesMut::with_capacity(status.encoded_len());
45
46 // Should never panic since `buf` is initialized with sufficient capacity
47 status.encode(&mut buf).unwrap();
48
49 buf.freeze()
50 }
51
52 /// Used to implement associated functions and methods on `tonic::Status`, that
53 /// allow the addition and extraction of standard error details. This trait is
54 /// sealed and not meant to be implemented outside of `tonic-types`.
55 pub trait StatusExt: crate::sealed::Sealed {
56 /// Generates a `tonic::Status` with error details obtained from an
57 /// [`ErrorDetails`] struct, and custom metadata.
58 ///
59 /// # Examples
60 ///
61 /// ```
62 /// use tonic::{metadata::MetadataMap, Code, Status};
63 /// use tonic_types::{ErrorDetails, StatusExt};
64 ///
65 /// let status = Status::with_error_details_and_metadata(
66 /// Code::InvalidArgument,
67 /// "bad request",
68 /// ErrorDetails::with_bad_request_violation("field", "description"),
69 /// MetadataMap::new()
70 /// );
71 /// ```
with_error_details_and_metadata( code: Code, message: impl Into<String>, details: ErrorDetails, metadata: MetadataMap, ) -> tonic::Status72 fn with_error_details_and_metadata(
73 code: Code,
74 message: impl Into<String>,
75 details: ErrorDetails,
76 metadata: MetadataMap,
77 ) -> tonic::Status;
78
79 /// Generates a `tonic::Status` with error details obtained from an
80 /// [`ErrorDetails`] struct.
81 ///
82 /// # Examples
83 ///
84 /// ```
85 /// use tonic::{Code, Status};
86 /// use tonic_types::{ErrorDetails, StatusExt};
87 ///
88 /// let status = Status::with_error_details(
89 /// Code::InvalidArgument,
90 /// "bad request",
91 /// ErrorDetails::with_bad_request_violation("field", "description"),
92 /// );
93 /// ```
with_error_details( code: Code, message: impl Into<String>, details: ErrorDetails, ) -> tonic::Status94 fn with_error_details(
95 code: Code,
96 message: impl Into<String>,
97 details: ErrorDetails,
98 ) -> tonic::Status;
99
100 /// Generates a `tonic::Status` with error details provided in a vector of
101 /// [`ErrorDetail`] enums, and custom metadata.
102 ///
103 /// # Examples
104 ///
105 /// ```
106 /// use tonic::{metadata::MetadataMap, Code, Status};
107 /// use tonic_types::{BadRequest, StatusExt};
108 ///
109 /// let status = Status::with_error_details_vec_and_metadata(
110 /// Code::InvalidArgument,
111 /// "bad request",
112 /// vec![
113 /// BadRequest::with_violation("field", "description").into(),
114 /// ],
115 /// MetadataMap::new()
116 /// );
117 /// ```
with_error_details_vec_and_metadata( code: Code, message: impl Into<String>, details: impl IntoIterator<Item = ErrorDetail>, metadata: MetadataMap, ) -> tonic::Status118 fn with_error_details_vec_and_metadata(
119 code: Code,
120 message: impl Into<String>,
121 details: impl IntoIterator<Item = ErrorDetail>,
122 metadata: MetadataMap,
123 ) -> tonic::Status;
124
125 /// Generates a `tonic::Status` with error details provided in a vector of
126 /// [`ErrorDetail`] enums.
127 ///
128 /// # Examples
129 ///
130 /// ```
131 /// use tonic::{Code, Status};
132 /// use tonic_types::{BadRequest, StatusExt};
133 ///
134 /// let status = Status::with_error_details_vec(
135 /// Code::InvalidArgument,
136 /// "bad request",
137 /// vec![
138 /// BadRequest::with_violation("field", "description").into(),
139 /// ]
140 /// );
141 /// ```
with_error_details_vec( code: Code, message: impl Into<String>, details: impl IntoIterator<Item = ErrorDetail>, ) -> tonic::Status142 fn with_error_details_vec(
143 code: Code,
144 message: impl Into<String>,
145 details: impl IntoIterator<Item = ErrorDetail>,
146 ) -> tonic::Status;
147
148 /// Can be used to check if the error details contained in `tonic::Status`
149 /// are malformed or not. Tries to get an [`ErrorDetails`] struct from a
150 /// `tonic::Status`. If some `prost::DecodeError` occurs, it will be
151 /// returned. If not debugging, consider using
152 /// [`StatusExt::get_error_details`] or
153 /// [`StatusExt::get_error_details_vec`].
154 ///
155 /// # Examples
156 ///
157 /// ```
158 /// use tonic::{Status, Response};
159 /// use tonic_types::StatusExt;
160 ///
161 /// fn handle_request_result<T>(req_result: Result<Response<T>, Status>) {
162 /// match req_result {
163 /// Ok(_) => {},
164 /// Err(status) => match status.check_error_details() {
165 /// Ok(err_details) => {
166 /// // Handle extracted details
167 /// }
168 /// Err(decode_error) => {
169 /// // Handle decode_error
170 /// }
171 /// }
172 /// };
173 /// }
174 /// ```
check_error_details(&self) -> Result<ErrorDetails, DecodeError>175 fn check_error_details(&self) -> Result<ErrorDetails, DecodeError>;
176
177 /// Get an [`ErrorDetails`] struct from `tonic::Status`. If some
178 /// `prost::DecodeError` occurs, an empty [`ErrorDetails`] struct will be
179 /// returned.
180 ///
181 /// # Examples
182 ///
183 /// ```
184 /// use tonic::{Status, Response};
185 /// use tonic_types::StatusExt;
186 ///
187 /// fn handle_request_result<T>(req_result: Result<Response<T>, Status>) {
188 /// match req_result {
189 /// Ok(_) => {},
190 /// Err(status) => {
191 /// let err_details = status.get_error_details();
192 /// if let Some(bad_request) = err_details.bad_request() {
193 /// // Handle bad_request details
194 /// }
195 /// // ...
196 /// }
197 /// };
198 /// }
199 /// ```
get_error_details(&self) -> ErrorDetails200 fn get_error_details(&self) -> ErrorDetails;
201
202 /// Can be used to check if the error details contained in `tonic::Status`
203 /// are malformed or not. Tries to get a vector of [`ErrorDetail`] enums
204 /// from a `tonic::Status`. If some `prost::DecodeError` occurs, it will be
205 /// returned. If not debugging, consider using
206 /// [`StatusExt::get_error_details_vec`] or
207 /// [`StatusExt::get_error_details`].
208 ///
209 /// # Examples
210 ///
211 /// ```
212 /// use tonic::{Status, Response};
213 /// use tonic_types::StatusExt;
214 ///
215 /// fn handle_request_result<T>(req_result: Result<Response<T>, Status>) {
216 /// match req_result {
217 /// Ok(_) => {},
218 /// Err(status) => match status.check_error_details_vec() {
219 /// Ok(err_details) => {
220 /// // Handle extracted details
221 /// }
222 /// Err(decode_error) => {
223 /// // Handle decode_error
224 /// }
225 /// }
226 /// };
227 /// }
228 /// ```
check_error_details_vec(&self) -> Result<Vec<ErrorDetail>, DecodeError>229 fn check_error_details_vec(&self) -> Result<Vec<ErrorDetail>, DecodeError>;
230
231 /// Get a vector of [`ErrorDetail`] enums from `tonic::Status`. If some
232 /// `prost::DecodeError` occurs, an empty vector will be returned.
233 ///
234 /// # Examples
235 ///
236 /// ```
237 /// use tonic::{Status, Response};
238 /// use tonic_types::{ErrorDetail, StatusExt};
239 ///
240 /// fn handle_request_result<T>(req_result: Result<Response<T>, Status>) {
241 /// match req_result {
242 /// Ok(_) => {},
243 /// Err(status) => {
244 /// let err_details = status.get_error_details_vec();
245 /// for err_detail in err_details.iter() {
246 /// match err_detail {
247 /// ErrorDetail::BadRequest(bad_request) => {
248 /// // Handle bad_request details
249 /// }
250 /// // ...
251 /// _ => {}
252 /// }
253 /// }
254 /// }
255 /// };
256 /// }
257 /// ```
get_error_details_vec(&self) -> Vec<ErrorDetail>258 fn get_error_details_vec(&self) -> Vec<ErrorDetail>;
259
260 /// Get first [`RetryInfo`] details found on `tonic::Status`, if any. If
261 /// some `prost::DecodeError` occurs, returns `None`.
262 ///
263 /// # Examples
264 ///
265 /// ```
266 /// use tonic::{Status, Response};
267 /// use tonic_types::StatusExt;
268 ///
269 /// fn handle_request_result<T>(req_result: Result<Response<T>, Status>) {
270 /// match req_result {
271 /// Ok(_) => {},
272 /// Err(status) => {
273 /// if let Some(retry_info) = status.get_details_retry_info() {
274 /// // Handle retry_info details
275 /// }
276 /// }
277 /// };
278 /// }
279 /// ```
get_details_retry_info(&self) -> Option<RetryInfo>280 fn get_details_retry_info(&self) -> Option<RetryInfo>;
281
282 /// Get first [`DebugInfo`] details found on `tonic::Status`, if any. If
283 /// some `prost::DecodeError` occurs, returns `None`.
284 ///
285 /// # Examples
286 ///
287 /// ```
288 /// use tonic::{Status, Response};
289 /// use tonic_types::StatusExt;
290 ///
291 /// fn handle_request_result<T>(req_result: Result<Response<T>, Status>) {
292 /// match req_result {
293 /// Ok(_) => {},
294 /// Err(status) => {
295 /// if let Some(debug_info) = status.get_details_debug_info() {
296 /// // Handle debug_info details
297 /// }
298 /// }
299 /// };
300 /// }
301 /// ```
get_details_debug_info(&self) -> Option<DebugInfo>302 fn get_details_debug_info(&self) -> Option<DebugInfo>;
303
304 /// Get first [`QuotaFailure`] details found on `tonic::Status`, if any.
305 /// If some `prost::DecodeError` occurs, returns `None`.
306 ///
307 /// # Examples
308 ///
309 /// ```
310 /// use tonic::{Status, Response};
311 /// use tonic_types::StatusExt;
312 ///
313 /// fn handle_request_result<T>(req_result: Result<Response<T>, Status>) {
314 /// match req_result {
315 /// Ok(_) => {},
316 /// Err(status) => {
317 /// if let Some(quota_failure) = status.get_details_quota_failure() {
318 /// // Handle quota_failure details
319 /// }
320 /// }
321 /// };
322 /// }
323 /// ```
get_details_quota_failure(&self) -> Option<QuotaFailure>324 fn get_details_quota_failure(&self) -> Option<QuotaFailure>;
325
326 /// Get first [`ErrorInfo`] details found on `tonic::Status`, if any. If
327 /// some `prost::DecodeError` occurs, returns `None`.
328 ///
329 /// # Examples
330 ///
331 /// ```
332 /// use tonic::{Status, Response};
333 /// use tonic_types::StatusExt;
334 ///
335 /// fn handle_request_result<T>(req_result: Result<Response<T>, Status>) {
336 /// match req_result {
337 /// Ok(_) => {},
338 /// Err(status) => {
339 /// if let Some(error_info) = status.get_details_error_info() {
340 /// // Handle error_info details
341 /// }
342 /// }
343 /// };
344 /// }
345 /// ```
get_details_error_info(&self) -> Option<ErrorInfo>346 fn get_details_error_info(&self) -> Option<ErrorInfo>;
347
348 /// Get first [`PreconditionFailure`] details found on `tonic::Status`,
349 /// if any. If some `prost::DecodeError` occurs, returns `None`.
350 ///
351 /// # Examples
352 ///
353 /// ```
354 /// use tonic::{Status, Response};
355 /// use tonic_types::StatusExt;
356 ///
357 /// fn handle_request_result<T>(req_result: Result<Response<T>, Status>) {
358 /// match req_result {
359 /// Ok(_) => {},
360 /// Err(status) => {
361 /// if let Some(precondition_failure) = status.get_details_precondition_failure() {
362 /// // Handle precondition_failure details
363 /// }
364 /// }
365 /// };
366 /// }
367 /// ```
get_details_precondition_failure(&self) -> Option<PreconditionFailure>368 fn get_details_precondition_failure(&self) -> Option<PreconditionFailure>;
369
370 /// Get first [`BadRequest`] details found on `tonic::Status`, if any. If
371 /// some `prost::DecodeError` occurs, returns `None`.
372 ///
373 /// # Examples
374 ///
375 /// ```
376 /// use tonic::{Status, Response};
377 /// use tonic_types::StatusExt;
378 ///
379 /// fn handle_request_result<T>(req_result: Result<Response<T>, Status>) {
380 /// match req_result {
381 /// Ok(_) => {},
382 /// Err(status) => {
383 /// if let Some(bad_request) = status.get_details_bad_request() {
384 /// // Handle bad_request details
385 /// }
386 /// }
387 /// };
388 /// }
389 /// ```
get_details_bad_request(&self) -> Option<BadRequest>390 fn get_details_bad_request(&self) -> Option<BadRequest>;
391
392 /// Get first [`RequestInfo`] details found on `tonic::Status`, if any.
393 /// If some `prost::DecodeError` occurs, returns `None`.
394 ///
395 /// # Examples
396 ///
397 /// ```
398 /// use tonic::{Status, Response};
399 /// use tonic_types::StatusExt;
400 ///
401 /// fn handle_request_result<T>(req_result: Result<Response<T>, Status>) {
402 /// match req_result {
403 /// Ok(_) => {},
404 /// Err(status) => {
405 /// if let Some(request_info) = status.get_details_request_info() {
406 /// // Handle request_info details
407 /// }
408 /// }
409 /// };
410 /// }
411 /// ```
get_details_request_info(&self) -> Option<RequestInfo>412 fn get_details_request_info(&self) -> Option<RequestInfo>;
413
414 /// Get first [`ResourceInfo`] details found on `tonic::Status`, if any.
415 /// If some `prost::DecodeError` occurs, returns `None`.
416 ///
417 /// # Examples
418 ///
419 /// ```
420 /// use tonic::{Status, Response};
421 /// use tonic_types::StatusExt;
422 ///
423 /// fn handle_request_result<T>(req_result: Result<Response<T>, Status>) {
424 /// match req_result {
425 /// Ok(_) => {},
426 /// Err(status) => {
427 /// if let Some(resource_info) = status.get_details_resource_info() {
428 /// // Handle resource_info details
429 /// }
430 /// }
431 /// };
432 /// }
433 /// ```
get_details_resource_info(&self) -> Option<ResourceInfo>434 fn get_details_resource_info(&self) -> Option<ResourceInfo>;
435
436 /// Get first [`Help`] details found on `tonic::Status`, if any. If some
437 /// `prost::DecodeError` occurs, returns `None`.
438 ///
439 /// # Examples
440 ///
441 /// ```
442 /// use tonic::{Status, Response};
443 /// use tonic_types::StatusExt;
444 ///
445 /// fn handle_request_result<T>(req_result: Result<Response<T>, Status>) {
446 /// match req_result {
447 /// Ok(_) => {},
448 /// Err(status) => {
449 /// if let Some(help) = status.get_details_help() {
450 /// // Handle help details
451 /// }
452 /// }
453 /// };
454 /// }
455 /// ```
get_details_help(&self) -> Option<Help>456 fn get_details_help(&self) -> Option<Help>;
457
458 /// Get first [`LocalizedMessage`] details found on `tonic::Status`, if
459 /// any. If some `prost::DecodeError` occurs, returns `None`.
460 ///
461 /// # Examples
462 ///
463 /// ```
464 /// use tonic::{Status, Response};
465 /// use tonic_types::StatusExt;
466 ///
467 /// fn handle_request_result<T>(req_result: Result<Response<T>, Status>) {
468 /// match req_result {
469 /// Ok(_) => {},
470 /// Err(status) => {
471 /// if let Some(localized_message) = status.get_details_localized_message() {
472 /// // Handle localized_message details
473 /// }
474 /// }
475 /// };
476 /// }
477 /// ```
get_details_localized_message(&self) -> Option<LocalizedMessage>478 fn get_details_localized_message(&self) -> Option<LocalizedMessage>;
479 }
480
481 impl crate::sealed::Sealed for tonic::Status {}
482
483 impl StatusExt for tonic::Status {
with_error_details_and_metadata( code: Code, message: impl Into<String>, details: ErrorDetails, metadata: MetadataMap, ) -> Self484 fn with_error_details_and_metadata(
485 code: Code,
486 message: impl Into<String>,
487 details: ErrorDetails,
488 metadata: MetadataMap,
489 ) -> Self {
490 let message: String = message.into();
491
492 let mut conv_details: Vec<Any> = Vec::with_capacity(10);
493
494 if let Some(retry_info) = details.retry_info {
495 conv_details.push(retry_info.into_any());
496 }
497
498 if let Some(debug_info) = details.debug_info {
499 conv_details.push(debug_info.into_any());
500 }
501
502 if let Some(quota_failure) = details.quota_failure {
503 conv_details.push(quota_failure.into_any());
504 }
505
506 if let Some(error_info) = details.error_info {
507 conv_details.push(error_info.into_any());
508 }
509
510 if let Some(precondition_failure) = details.precondition_failure {
511 conv_details.push(precondition_failure.into_any());
512 }
513
514 if let Some(bad_request) = details.bad_request {
515 conv_details.push(bad_request.into_any());
516 }
517
518 if let Some(request_info) = details.request_info {
519 conv_details.push(request_info.into_any());
520 }
521
522 if let Some(resource_info) = details.resource_info {
523 conv_details.push(resource_info.into_any());
524 }
525
526 if let Some(help) = details.help {
527 conv_details.push(help.into_any());
528 }
529
530 if let Some(localized_message) = details.localized_message {
531 conv_details.push(localized_message.into_any());
532 }
533
534 let details = gen_details_bytes(code, &message, conv_details);
535
536 tonic::Status::with_details_and_metadata(code, message, details, metadata)
537 }
538
with_error_details(code: Code, message: impl Into<String>, details: ErrorDetails) -> Self539 fn with_error_details(code: Code, message: impl Into<String>, details: ErrorDetails) -> Self {
540 tonic::Status::with_error_details_and_metadata(code, message, details, MetadataMap::new())
541 }
542
with_error_details_vec_and_metadata( code: Code, message: impl Into<String>, details: impl IntoIterator<Item = ErrorDetail>, metadata: MetadataMap, ) -> Self543 fn with_error_details_vec_and_metadata(
544 code: Code,
545 message: impl Into<String>,
546 details: impl IntoIterator<Item = ErrorDetail>,
547 metadata: MetadataMap,
548 ) -> Self {
549 let message: String = message.into();
550
551 let mut conv_details: Vec<Any> = Vec::new();
552
553 for error_detail in details.into_iter() {
554 match error_detail {
555 ErrorDetail::RetryInfo(retry_info) => {
556 conv_details.push(retry_info.into_any());
557 }
558 ErrorDetail::DebugInfo(debug_info) => {
559 conv_details.push(debug_info.into_any());
560 }
561 ErrorDetail::QuotaFailure(quota_failure) => {
562 conv_details.push(quota_failure.into_any());
563 }
564 ErrorDetail::ErrorInfo(error_info) => {
565 conv_details.push(error_info.into_any());
566 }
567 ErrorDetail::PreconditionFailure(prec_failure) => {
568 conv_details.push(prec_failure.into_any());
569 }
570 ErrorDetail::BadRequest(bad_req) => {
571 conv_details.push(bad_req.into_any());
572 }
573 ErrorDetail::RequestInfo(req_info) => {
574 conv_details.push(req_info.into_any());
575 }
576 ErrorDetail::ResourceInfo(res_info) => {
577 conv_details.push(res_info.into_any());
578 }
579 ErrorDetail::Help(help) => {
580 conv_details.push(help.into_any());
581 }
582 ErrorDetail::LocalizedMessage(loc_message) => {
583 conv_details.push(loc_message.into_any());
584 }
585 }
586 }
587
588 let details = gen_details_bytes(code, &message, conv_details);
589
590 tonic::Status::with_details_and_metadata(code, message, details, metadata)
591 }
592
with_error_details_vec( code: Code, message: impl Into<String>, details: impl IntoIterator<Item = ErrorDetail>, ) -> Self593 fn with_error_details_vec(
594 code: Code,
595 message: impl Into<String>,
596 details: impl IntoIterator<Item = ErrorDetail>,
597 ) -> Self {
598 tonic::Status::with_error_details_vec_and_metadata(
599 code,
600 message,
601 details,
602 MetadataMap::new(),
603 )
604 }
605
check_error_details(&self) -> Result<ErrorDetails, DecodeError>606 fn check_error_details(&self) -> Result<ErrorDetails, DecodeError> {
607 let status = pb::Status::decode(self.details())?;
608
609 status.check_error_details()
610 }
611
get_error_details(&self) -> ErrorDetails612 fn get_error_details(&self) -> ErrorDetails {
613 self.check_error_details().unwrap_or_default()
614 }
615
check_error_details_vec(&self) -> Result<Vec<ErrorDetail>, DecodeError>616 fn check_error_details_vec(&self) -> Result<Vec<ErrorDetail>, DecodeError> {
617 let status = pb::Status::decode(self.details())?;
618
619 status.check_error_details_vec()
620 }
621
get_error_details_vec(&self) -> Vec<ErrorDetail>622 fn get_error_details_vec(&self) -> Vec<ErrorDetail> {
623 self.check_error_details_vec().unwrap_or_default()
624 }
625
get_details_retry_info(&self) -> Option<RetryInfo>626 fn get_details_retry_info(&self) -> Option<RetryInfo> {
627 let status = pb::Status::decode(self.details()).ok()?;
628
629 status.get_details_retry_info()
630 }
631
get_details_debug_info(&self) -> Option<DebugInfo>632 fn get_details_debug_info(&self) -> Option<DebugInfo> {
633 let status = pb::Status::decode(self.details()).ok()?;
634
635 status.get_details_debug_info()
636 }
637
get_details_quota_failure(&self) -> Option<QuotaFailure>638 fn get_details_quota_failure(&self) -> Option<QuotaFailure> {
639 let status = pb::Status::decode(self.details()).ok()?;
640
641 status.get_details_quota_failure()
642 }
643
get_details_error_info(&self) -> Option<ErrorInfo>644 fn get_details_error_info(&self) -> Option<ErrorInfo> {
645 let status = pb::Status::decode(self.details()).ok()?;
646
647 status.get_details_error_info()
648 }
649
get_details_precondition_failure(&self) -> Option<PreconditionFailure>650 fn get_details_precondition_failure(&self) -> Option<PreconditionFailure> {
651 let status = pb::Status::decode(self.details()).ok()?;
652
653 status.get_details_precondition_failure()
654 }
655
get_details_bad_request(&self) -> Option<BadRequest>656 fn get_details_bad_request(&self) -> Option<BadRequest> {
657 let status = pb::Status::decode(self.details()).ok()?;
658
659 status.get_details_bad_request()
660 }
661
get_details_request_info(&self) -> Option<RequestInfo>662 fn get_details_request_info(&self) -> Option<RequestInfo> {
663 let status = pb::Status::decode(self.details()).ok()?;
664
665 status.get_details_request_info()
666 }
667
get_details_resource_info(&self) -> Option<ResourceInfo>668 fn get_details_resource_info(&self) -> Option<ResourceInfo> {
669 let status = pb::Status::decode(self.details()).ok()?;
670
671 status.get_details_resource_info()
672 }
673
get_details_help(&self) -> Option<Help>674 fn get_details_help(&self) -> Option<Help> {
675 let status = pb::Status::decode(self.details()).ok()?;
676
677 status.get_details_help()
678 }
679
get_details_localized_message(&self) -> Option<LocalizedMessage>680 fn get_details_localized_message(&self) -> Option<LocalizedMessage> {
681 let status = pb::Status::decode(self.details()).ok()?;
682
683 status.get_details_localized_message()
684 }
685 }
686
687 impl crate::sealed::Sealed for pb::Status {}
688
689 /// Used to implement associated functions and methods on `pb::Status`, that
690 /// allow the extraction of standard error details. This trait is
691 /// sealed and not meant to be implemented outside of `tonic-types`.
692 pub trait RpcStatusExt: crate::sealed::Sealed {
693 /// Can be used to check if the error details contained in `pb::Status`
694 /// are malformed or not. Tries to get an [`ErrorDetails`] struct from a
695 /// `pb::Status`. If some `prost::DecodeError` occurs, it will be
696 /// returned. If not debugging, consider using
697 /// [`RpcStatusExt::get_error_details`] or
698 /// [`RpcStatusExt::get_error_details_vec`].
check_error_details(&self) -> Result<ErrorDetails, DecodeError>699 fn check_error_details(&self) -> Result<ErrorDetails, DecodeError>;
700
701 /// Get an [`ErrorDetails`] struct from `pb::Status`. If some
702 /// `prost::DecodeError` occurs, an empty [`ErrorDetails`] struct will be
703 /// returned.
get_error_details(&self) -> ErrorDetails704 fn get_error_details(&self) -> ErrorDetails;
705
706 /// Can be used to check if the error details contained in `pb::Status`
707 /// are malformed or not. Tries to get a vector of [`ErrorDetail`] enums
708 /// from a `pb::Status`. If some `prost::DecodeError` occurs, it will be
709 /// returned. If not debugging, consider using
710 /// [`StatusExt::get_error_details_vec`] or
711 /// [`StatusExt::get_error_details`].
check_error_details_vec(&self) -> Result<Vec<ErrorDetail>, DecodeError>712 fn check_error_details_vec(&self) -> Result<Vec<ErrorDetail>, DecodeError>;
713
714 /// Get a vector of [`ErrorDetail`] enums from `pb::Status`. If some
715 /// `prost::DecodeError` occurs, an empty vector will be returned.
get_error_details_vec(&self) -> Vec<ErrorDetail>716 fn get_error_details_vec(&self) -> Vec<ErrorDetail>;
717
718 /// Get first [`RetryInfo`] details found on `pb::Status`, if any. If
719 /// some `prost::DecodeError` occurs, returns `None`.
get_details_retry_info(&self) -> Option<RetryInfo>720 fn get_details_retry_info(&self) -> Option<RetryInfo>;
721
722 /// Get first [`DebugInfo`] details found on `pb::Status`, if any. If
723 /// some `prost::DecodeError` occurs, returns `None`.
get_details_debug_info(&self) -> Option<DebugInfo>724 fn get_details_debug_info(&self) -> Option<DebugInfo>;
725
726 /// Get first [`QuotaFailure`] details found on `pb::Status`, if any.
727 /// If some `prost::DecodeError` occurs, returns `None`.
get_details_quota_failure(&self) -> Option<QuotaFailure>728 fn get_details_quota_failure(&self) -> Option<QuotaFailure>;
729
730 /// Get first [`ErrorInfo`] details found on `pb::Status`, if any. If
731 /// some `prost::DecodeError` occurs, returns `None`.
get_details_error_info(&self) -> Option<ErrorInfo>732 fn get_details_error_info(&self) -> Option<ErrorInfo>;
733
734 /// Get first [`PreconditionFailure`] details found on `pb::Status`,
735 /// if any. If some `prost::DecodeError` occurs, returns `None`.
get_details_precondition_failure(&self) -> Option<PreconditionFailure>736 fn get_details_precondition_failure(&self) -> Option<PreconditionFailure>;
737
738 /// Get first [`BadRequest`] details found on `pb::Status`, if any. If
739 /// some `prost::DecodeError` occurs, returns `None`.
get_details_bad_request(&self) -> Option<BadRequest>740 fn get_details_bad_request(&self) -> Option<BadRequest>;
741
742 /// Get first [`RequestInfo`] details found on `pb::Status`, if any.
743 /// If some `prost::DecodeError` occurs, returns `None`.
get_details_request_info(&self) -> Option<RequestInfo>744 fn get_details_request_info(&self) -> Option<RequestInfo>;
745
746 /// Get first [`ResourceInfo`] details found on `pb::Status`, if any.
747 /// If some `prost::DecodeError` occurs, returns `None`.
get_details_resource_info(&self) -> Option<ResourceInfo>748 fn get_details_resource_info(&self) -> Option<ResourceInfo>;
749
750 /// Get first [`Help`] details found on `pb::Status`, if any. If some
751 /// `prost::DecodeError` occurs, returns `None`.
get_details_help(&self) -> Option<Help>752 fn get_details_help(&self) -> Option<Help>;
753
754 /// Get first [`LocalizedMessage`] details found on `pb::Status`, if
755 /// any. If some `prost::DecodeError` occurs, returns `None`.
get_details_localized_message(&self) -> Option<LocalizedMessage>756 fn get_details_localized_message(&self) -> Option<LocalizedMessage>;
757 }
758
759 impl RpcStatusExt for pb::Status {
check_error_details(&self) -> Result<ErrorDetails, DecodeError>760 fn check_error_details(&self) -> Result<ErrorDetails, DecodeError> {
761 let mut details = ErrorDetails::new();
762
763 for any in self.details.iter() {
764 match any.type_url.as_str() {
765 RetryInfo::TYPE_URL => {
766 details.retry_info = Some(RetryInfo::from_any_ref(any)?);
767 }
768 DebugInfo::TYPE_URL => {
769 details.debug_info = Some(DebugInfo::from_any_ref(any)?);
770 }
771 QuotaFailure::TYPE_URL => {
772 details.quota_failure = Some(QuotaFailure::from_any_ref(any)?);
773 }
774 ErrorInfo::TYPE_URL => {
775 details.error_info = Some(ErrorInfo::from_any_ref(any)?);
776 }
777 PreconditionFailure::TYPE_URL => {
778 details.precondition_failure = Some(PreconditionFailure::from_any_ref(any)?);
779 }
780 BadRequest::TYPE_URL => {
781 details.bad_request = Some(BadRequest::from_any_ref(any)?);
782 }
783 RequestInfo::TYPE_URL => {
784 details.request_info = Some(RequestInfo::from_any_ref(any)?);
785 }
786 ResourceInfo::TYPE_URL => {
787 details.resource_info = Some(ResourceInfo::from_any_ref(any)?);
788 }
789 Help::TYPE_URL => {
790 details.help = Some(Help::from_any_ref(any)?);
791 }
792 LocalizedMessage::TYPE_URL => {
793 details.localized_message = Some(LocalizedMessage::from_any_ref(any)?);
794 }
795 _ => {}
796 }
797 }
798
799 Ok(details)
800 }
801
get_error_details(&self) -> ErrorDetails802 fn get_error_details(&self) -> ErrorDetails {
803 self.check_error_details().unwrap_or_default()
804 }
805
check_error_details_vec(&self) -> Result<Vec<ErrorDetail>, DecodeError>806 fn check_error_details_vec(&self) -> Result<Vec<ErrorDetail>, DecodeError> {
807 let mut details: Vec<ErrorDetail> = Vec::with_capacity(self.details.len());
808
809 for any in self.details.iter() {
810 match any.type_url.as_str() {
811 RetryInfo::TYPE_URL => {
812 details.push(RetryInfo::from_any_ref(any)?.into());
813 }
814 DebugInfo::TYPE_URL => {
815 details.push(DebugInfo::from_any_ref(any)?.into());
816 }
817 QuotaFailure::TYPE_URL => {
818 details.push(QuotaFailure::from_any_ref(any)?.into());
819 }
820 ErrorInfo::TYPE_URL => {
821 details.push(ErrorInfo::from_any_ref(any)?.into());
822 }
823 PreconditionFailure::TYPE_URL => {
824 details.push(PreconditionFailure::from_any_ref(any)?.into());
825 }
826 BadRequest::TYPE_URL => {
827 details.push(BadRequest::from_any_ref(any)?.into());
828 }
829 RequestInfo::TYPE_URL => {
830 details.push(RequestInfo::from_any_ref(any)?.into());
831 }
832 ResourceInfo::TYPE_URL => {
833 details.push(ResourceInfo::from_any_ref(any)?.into());
834 }
835 Help::TYPE_URL => {
836 details.push(Help::from_any_ref(any)?.into());
837 }
838 LocalizedMessage::TYPE_URL => {
839 details.push(LocalizedMessage::from_any_ref(any)?.into());
840 }
841 _ => {}
842 }
843 }
844
845 Ok(details)
846 }
847
get_error_details_vec(&self) -> Vec<ErrorDetail>848 fn get_error_details_vec(&self) -> Vec<ErrorDetail> {
849 self.check_error_details_vec().unwrap_or_default()
850 }
851
get_details_retry_info(&self) -> Option<RetryInfo>852 fn get_details_retry_info(&self) -> Option<RetryInfo> {
853 for any in self.details.iter() {
854 if any.type_url.as_str() == RetryInfo::TYPE_URL {
855 if let Ok(detail) = RetryInfo::from_any_ref(any) {
856 return Some(detail);
857 }
858 }
859 }
860
861 None
862 }
863
get_details_debug_info(&self) -> Option<DebugInfo>864 fn get_details_debug_info(&self) -> Option<DebugInfo> {
865 for any in self.details.iter() {
866 if any.type_url.as_str() == DebugInfo::TYPE_URL {
867 if let Ok(detail) = DebugInfo::from_any_ref(any) {
868 return Some(detail);
869 }
870 }
871 }
872
873 None
874 }
875
get_details_quota_failure(&self) -> Option<QuotaFailure>876 fn get_details_quota_failure(&self) -> Option<QuotaFailure> {
877 for any in self.details.iter() {
878 if any.type_url.as_str() == QuotaFailure::TYPE_URL {
879 if let Ok(detail) = QuotaFailure::from_any_ref(any) {
880 return Some(detail);
881 }
882 }
883 }
884
885 None
886 }
887
get_details_error_info(&self) -> Option<ErrorInfo>888 fn get_details_error_info(&self) -> Option<ErrorInfo> {
889 for any in self.details.iter() {
890 if any.type_url.as_str() == ErrorInfo::TYPE_URL {
891 if let Ok(detail) = ErrorInfo::from_any_ref(any) {
892 return Some(detail);
893 }
894 }
895 }
896
897 None
898 }
899
get_details_precondition_failure(&self) -> Option<PreconditionFailure>900 fn get_details_precondition_failure(&self) -> Option<PreconditionFailure> {
901 for any in self.details.iter() {
902 if any.type_url.as_str() == PreconditionFailure::TYPE_URL {
903 if let Ok(detail) = PreconditionFailure::from_any_ref(any) {
904 return Some(detail);
905 }
906 }
907 }
908
909 None
910 }
911
get_details_bad_request(&self) -> Option<BadRequest>912 fn get_details_bad_request(&self) -> Option<BadRequest> {
913 for any in self.details.iter() {
914 if any.type_url.as_str() == BadRequest::TYPE_URL {
915 if let Ok(detail) = BadRequest::from_any_ref(any) {
916 return Some(detail);
917 }
918 }
919 }
920
921 None
922 }
923
get_details_request_info(&self) -> Option<RequestInfo>924 fn get_details_request_info(&self) -> Option<RequestInfo> {
925 for any in self.details.iter() {
926 if any.type_url.as_str() == RequestInfo::TYPE_URL {
927 if let Ok(detail) = RequestInfo::from_any_ref(any) {
928 return Some(detail);
929 }
930 }
931 }
932
933 None
934 }
935
get_details_resource_info(&self) -> Option<ResourceInfo>936 fn get_details_resource_info(&self) -> Option<ResourceInfo> {
937 for any in self.details.iter() {
938 if any.type_url.as_str() == ResourceInfo::TYPE_URL {
939 if let Ok(detail) = ResourceInfo::from_any_ref(any) {
940 return Some(detail);
941 }
942 }
943 }
944
945 None
946 }
947
get_details_help(&self) -> Option<Help>948 fn get_details_help(&self) -> Option<Help> {
949 for any in self.details.iter() {
950 if any.type_url.as_str() == Help::TYPE_URL {
951 if let Ok(detail) = Help::from_any_ref(any) {
952 return Some(detail);
953 }
954 }
955 }
956
957 None
958 }
959
get_details_localized_message(&self) -> Option<LocalizedMessage>960 fn get_details_localized_message(&self) -> Option<LocalizedMessage> {
961 for any in self.details.iter() {
962 if any.type_url.as_str() == LocalizedMessage::TYPE_URL {
963 if let Ok(detail) = LocalizedMessage::from_any_ref(any) {
964 return Some(detail);
965 }
966 }
967 }
968
969 None
970 }
971 }
972
973 #[cfg(test)]
974 mod tests {
975 use std::{collections::HashMap, time::Duration};
976 use tonic::{Code, Status};
977
978 use super::{
979 BadRequest, DebugInfo, ErrorDetails, ErrorInfo, Help, LocalizedMessage,
980 PreconditionFailure, QuotaFailure, RequestInfo, ResourceInfo, RetryInfo, StatusExt,
981 };
982
983 #[test]
gen_status_with_details()984 fn gen_status_with_details() {
985 let mut metadata = HashMap::new();
986 metadata.insert("limitPerRequest".into(), "100".into());
987
988 let mut err_details = ErrorDetails::new();
989
990 err_details
991 .set_retry_info(Some(Duration::from_secs(5)))
992 .set_debug_info(
993 vec!["trace3".into(), "trace2".into(), "trace1".into()],
994 "details",
995 )
996 .add_quota_failure_violation("clientip:<ip address>", "description")
997 .set_error_info("SOME_INFO", "example.local", metadata.clone())
998 .add_precondition_failure_violation("TOS", "example.local", "description")
999 .add_bad_request_violation("field", "description")
1000 .set_request_info("request-id", "some-request-data")
1001 .set_resource_info("resource-type", "resource-name", "owner", "description")
1002 .add_help_link("link to resource", "resource.example.local")
1003 .set_localized_message("en-US", "message for the user");
1004
1005 let fmt_details = format!("{:?}", err_details);
1006
1007 let err_details_vec = vec![
1008 RetryInfo::new(Some(Duration::from_secs(5))).into(),
1009 DebugInfo::new(
1010 vec!["trace3".into(), "trace2".into(), "trace1".into()],
1011 "details",
1012 )
1013 .into(),
1014 QuotaFailure::with_violation("clientip:<ip address>", "description").into(),
1015 ErrorInfo::new("SOME_INFO", "example.local", metadata).into(),
1016 PreconditionFailure::with_violation("TOS", "example.local", "description").into(),
1017 BadRequest::with_violation("field", "description").into(),
1018 RequestInfo::new("request-id", "some-request-data").into(),
1019 ResourceInfo::new("resource-type", "resource-name", "owner", "description").into(),
1020 Help::with_link("link to resource", "resource.example.local").into(),
1021 LocalizedMessage::new("en-US", "message for the user").into(),
1022 ];
1023
1024 let fmt_details_vec = format!("{:?}", err_details_vec);
1025
1026 let status_from_struct = Status::with_error_details(
1027 Code::InvalidArgument,
1028 "error with bad request details",
1029 err_details,
1030 );
1031
1032 let status_from_vec = Status::with_error_details_vec(
1033 Code::InvalidArgument,
1034 "error with bad request details",
1035 err_details_vec,
1036 );
1037
1038 let ext_details = match status_from_vec.check_error_details() {
1039 Ok(ext_details) => ext_details,
1040 Err(err) => panic!(
1041 "Error extracting details struct from status_from_vec: {:?}",
1042 err
1043 ),
1044 };
1045
1046 let fmt_ext_details = format!("{:?}", ext_details);
1047
1048 assert!(
1049 fmt_ext_details.eq(&fmt_details),
1050 "Extracted details struct differs from original details struct"
1051 );
1052
1053 let ext_details_vec = match status_from_struct.check_error_details_vec() {
1054 Ok(ext_details) => ext_details,
1055 Err(err) => panic!(
1056 "Error extracting details_vec from status_from_struct: {:?}",
1057 err
1058 ),
1059 };
1060
1061 let fmt_ext_details_vec = format!("{:?}", ext_details_vec);
1062
1063 assert!(
1064 fmt_ext_details_vec.eq(&fmt_details_vec),
1065 "Extracted details vec differs from original details vec"
1066 );
1067 }
1068 }
1069