1 //! A collection of useful protobuf types that can be used with `tonic`. 2 //! 3 //! This crate also introduces the [`StatusExt`] trait and implements it in 4 //! [`tonic::Status`], allowing the implementation of the 5 //! [gRPC Richer Error Model] with [`tonic`] in a convenient way. 6 //! 7 //! # Usage 8 //! 9 //! Useful protobuf types are available through the [`pb`] module. They can be 10 //! imported and worked with directly. 11 //! 12 //! The [`StatusExt`] trait adds associated functions to [`tonic::Status`] that 13 //! can be used on the server side to create a status with error details, which 14 //! can then be returned to gRPC clients. Moreover, the trait also adds methods 15 //! to [`tonic::Status`] that can be used by a tonic client to extract error 16 //! details, and handle them with ease. 17 //! 18 //! # Getting Started 19 //! 20 //! ```toml 21 //! [dependencies] 22 //! tonic = <tonic-version> 23 //! tonic-types = <tonic-types-version> 24 //! ``` 25 //! 26 //! # Examples 27 //! 28 //! The examples bellow cover a basic use case of the [gRPC Richer Error Model]. 29 //! More complete server and client implementations are provided in the 30 //! **Richer Error example**, located in the main repo [examples] directory. 31 //! 32 //! ## Server Side: Generating [`tonic::Status`] with an [`ErrorDetails`] struct 33 //! 34 //! ``` 35 //! use tonic::{Code, Status}; 36 //! use tonic_types::{ErrorDetails, StatusExt}; 37 //! 38 //! # async fn endpoint() -> Result<tonic::Response<()>, Status> { 39 //! // ... 40 //! // Inside a gRPC server endpoint that returns `Result<Response<T>, Status>` 41 //! 42 //! // Create empty `ErrorDetails` struct 43 //! let mut err_details = ErrorDetails::new(); 44 //! 45 //! // Add error details conditionally 46 //! # let some_condition = true; 47 //! if some_condition { 48 //! err_details.add_bad_request_violation( 49 //! "field_a", 50 //! "description of why the field_a is invalid" 51 //! ); 52 //! } 53 //! 54 //! # let other_condition = true; 55 //! if other_condition { 56 //! err_details.add_bad_request_violation( 57 //! "field_b", 58 //! "description of why the field_b is invalid", 59 //! ); 60 //! } 61 //! 62 //! // Check if any error details were set and return error status if so 63 //! if err_details.has_bad_request_violations() { 64 //! // Add additional error details if necessary 65 //! err_details 66 //! .add_help_link("description of link", "https://resource.example.local") 67 //! .set_localized_message("en-US", "message for the user"); 68 //! 69 //! let status = Status::with_error_details( 70 //! Code::InvalidArgument, 71 //! "bad request", 72 //! err_details, 73 //! ); 74 //! return Err(status); 75 //! } 76 //! 77 //! // Handle valid request 78 //! // ... 79 //! # Ok(tonic::Response::new(())) 80 //! # } 81 //! ``` 82 //! 83 //! ## Client Side: Extracting an [`ErrorDetails`] struct from `tonic::Status` 84 //! 85 //! ``` 86 //! use tonic::{Response, Status}; 87 //! use tonic_types::StatusExt; 88 //! 89 //! // ... 90 //! // Where `req_result` was returned by a gRPC client endpoint method 91 //! fn handle_request_result<T>(req_result: Result<Response<T>, Status>) { 92 //! match req_result { 93 //! Ok(response) => { 94 //! // Handle successful response 95 //! }, 96 //! Err(status) => { 97 //! let err_details = status.get_error_details(); 98 //! if let Some(bad_request) = err_details.bad_request() { 99 //! // Handle bad_request details 100 //! } 101 //! if let Some(help) = err_details.help() { 102 //! // Handle help details 103 //! } 104 //! if let Some(localized_message) = err_details.localized_message() { 105 //! // Handle localized_message details 106 //! } 107 //! } 108 //! }; 109 //! } 110 //! ``` 111 //! 112 //! # Working with different error message types 113 //! 114 //! Multiple examples are provided at the [`ErrorDetails`] doc. Instructions 115 //! about how to use the fields of the standard error message types correctly 116 //! are provided at [error_details.proto]. 117 //! 118 //! # Alternative `tonic::Status` associated functions and methods 119 //! 120 //! In the [`StatusExt`] doc, an alternative way of interacting with 121 //! [`tonic::Status`] is presented, using vectors of error details structs 122 //! wrapped with the [`ErrorDetail`] enum. This approach can provide more 123 //! control over the vector of standard error messages that will be generated or 124 //! that was received, if necessary. To see how to adopt this approach, please 125 //! check the [`StatusExt::with_error_details_vec`] and 126 //! [`StatusExt::get_error_details_vec`] docs, and also the main repo's 127 //! [Richer Error example] directory. 128 //! 129 //! Besides that, multiple examples with alternative error details extraction 130 //! methods are provided in the [`StatusExt`] doc, which can be specially 131 //! useful if only one type of standard error message is being handled by the 132 //! client. For example, using [`StatusExt::get_details_bad_request`] is a 133 //! more direct way of extracting a [`BadRequest`] error message from 134 //! [`tonic::Status`]. 135 //! 136 //! [`tonic::Status`]: https://docs.rs/tonic/latest/tonic/struct.Status.html 137 //! [`tonic`]: https://docs.rs/tonic/latest/tonic/ 138 //! [gRPC Richer Error Model]: https://www.grpc.io/docs/guides/error/ 139 //! [examples]: https://github.com/hyperium/tonic/tree/master/examples 140 //! [error_details.proto]: https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto 141 //! [Richer Error example]: https://github.com/hyperium/tonic/tree/master/examples/src/richer-error 142 143 #![doc( 144 html_logo_url = "https://raw.githubusercontent.com/tokio-rs/website/master/public/img/icons/tonic.svg" 145 )] 146 #![doc(issue_tracker_base_url = "https://github.com/hyperium/tonic/issues/")] 147 148 mod generated { 149 #![allow(unreachable_pub)] 150 #![allow(rustdoc::invalid_html_tags)] 151 #[rustfmt::skip] 152 pub mod google_rpc; 153 #[rustfmt::skip] 154 pub mod types_fds; 155 156 pub use types_fds::FILE_DESCRIPTOR_SET; 157 158 #[cfg(test)] 159 mod tests { 160 use super::FILE_DESCRIPTOR_SET; 161 use prost::Message as _; 162 163 #[test] file_descriptor_set_is_valid()164 fn file_descriptor_set_is_valid() { 165 prost_types::FileDescriptorSet::decode(FILE_DESCRIPTOR_SET).unwrap(); 166 } 167 } 168 } 169 170 /// Useful protobuf types 171 pub mod pb { 172 pub use crate::generated::{google_rpc::*, FILE_DESCRIPTOR_SET}; 173 } 174 175 pub use pb::Status; 176 177 mod richer_error; 178 179 pub use richer_error::{ 180 BadRequest, DebugInfo, ErrorDetail, ErrorDetails, ErrorInfo, FieldViolation, Help, HelpLink, 181 LocalizedMessage, PreconditionFailure, PreconditionViolation, QuotaFailure, QuotaViolation, 182 RequestInfo, ResourceInfo, RetryInfo, RpcStatusExt, StatusExt, 183 }; 184 185 mod sealed { 186 pub trait Sealed {} 187 } 188