xref: /tonic/tonic-types/src/richer_error/mod.rs (revision 62a26db4)
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