tonic_types/
lib.rs

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 below 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
148mod 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]
164        fn file_descriptor_set_is_valid() {
165            prost_types::FileDescriptorSet::decode(FILE_DESCRIPTOR_SET).unwrap();
166        }
167    }
168}
169
170/// Useful protobuf types
171pub mod pb {
172    pub use crate::generated::{google_rpc::*, FILE_DESCRIPTOR_SET};
173}
174
175pub use pb::Status;
176
177mod richer_error;
178
179pub 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
185mod sealed {
186    pub trait Sealed {}
187}