feat: Add Status constructors (#137)
Co-Authored-By: Lucio Franco <[email protected]>
This commit is contained in:
committed by
Lucio Franco
co-authored by
Lucio Franco
parent
85060500f3
commit
997241c43f
+204
-7
@@ -9,6 +9,17 @@ const GRPC_STATUS_MESSAGE_HEADER: &str = "grpc-message";
|
|||||||
const GRPC_STATUS_DETAILS_HEADER: &str = "grpc-status-details-bin";
|
const GRPC_STATUS_DETAILS_HEADER: &str = "grpc-status-details-bin";
|
||||||
|
|
||||||
/// A gRPC status describing the result of an RPC call.
|
/// A gRPC status describing the result of an RPC call.
|
||||||
|
///
|
||||||
|
/// Values can be created using the `new` function or one of the specialized
|
||||||
|
/// associated functions.
|
||||||
|
/// ```rust
|
||||||
|
/// # use tonic::{Status, Code};
|
||||||
|
/// let status1 = Status::new(Code::InvalidArgument, "name is invalid");
|
||||||
|
/// let status2 = Status::invalid_argument("name is invalid");
|
||||||
|
///
|
||||||
|
/// assert_eq!(status1.code(), Code::InvalidArgument);
|
||||||
|
/// assert_eq!(status1.code(), status2.code());
|
||||||
|
/// ```
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Status {
|
pub struct Status {
|
||||||
/// The gRPC status code, found in the `grpc-status` header.
|
/// The gRPC status code, found in the `grpc-status` header.
|
||||||
@@ -25,24 +36,56 @@ pub struct Status {
|
|||||||
///
|
///
|
||||||
/// [gRPC status codes]: https://github.com/grpc/grpc/blob/master/doc/statuscodes.md#status-codes-and-their-use-in-grpc
|
/// [gRPC status codes]: https://github.com/grpc/grpc/blob/master/doc/statuscodes.md#status-codes-and-their-use-in-grpc
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub enum Code {
|
pub enum Code {
|
||||||
|
/// The operation completed successfully.
|
||||||
Ok = 0,
|
Ok = 0,
|
||||||
|
|
||||||
|
/// The operation was cancelled.
|
||||||
Cancelled = 1,
|
Cancelled = 1,
|
||||||
|
|
||||||
|
/// Unknown error.
|
||||||
Unknown = 2,
|
Unknown = 2,
|
||||||
|
|
||||||
|
/// Client specified an invalid argument.
|
||||||
InvalidArgument = 3,
|
InvalidArgument = 3,
|
||||||
|
|
||||||
|
/// Deadline expired before operation could complete.
|
||||||
DeadlineExceeded = 4,
|
DeadlineExceeded = 4,
|
||||||
|
|
||||||
|
/// Some requested entity was not found.
|
||||||
NotFound = 5,
|
NotFound = 5,
|
||||||
|
|
||||||
|
/// Some entity that we attempted to create already exists.
|
||||||
AlreadyExists = 6,
|
AlreadyExists = 6,
|
||||||
|
|
||||||
|
/// The caller does not have permission to execute the specified operation.
|
||||||
PermissionDenied = 7,
|
PermissionDenied = 7,
|
||||||
|
|
||||||
|
/// Some resource has been exhausted.
|
||||||
ResourceExhausted = 8,
|
ResourceExhausted = 8,
|
||||||
|
|
||||||
|
/// The system is not in a state required for the operation's execution.
|
||||||
FailedPrecondition = 9,
|
FailedPrecondition = 9,
|
||||||
|
|
||||||
|
/// The operation was aborted.
|
||||||
Aborted = 10,
|
Aborted = 10,
|
||||||
|
|
||||||
|
/// Operation was attempted past the valid range.
|
||||||
OutOfRange = 11,
|
OutOfRange = 11,
|
||||||
|
|
||||||
|
/// Operation is not implemented or not supported.
|
||||||
Unimplemented = 12,
|
Unimplemented = 12,
|
||||||
|
|
||||||
|
/// Internal error.
|
||||||
Internal = 13,
|
Internal = 13,
|
||||||
|
|
||||||
|
/// The service is currently unavailable.
|
||||||
Unavailable = 14,
|
Unavailable = 14,
|
||||||
|
|
||||||
|
/// Unrecoverable data loss or corruption.
|
||||||
DataLoss = 15,
|
DataLoss = 15,
|
||||||
|
|
||||||
|
/// The request does not have valid authentication credentials
|
||||||
Unauthenticated = 16,
|
Unauthenticated = 16,
|
||||||
|
|
||||||
// New Codes may be added in the future, so never exhaustively match!
|
// New Codes may be added in the future, so never exhaustively match!
|
||||||
@@ -62,13 +105,140 @@ impl Status {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new `Unimplemented` status with the associated message.
|
/// The operation completed successfully.
|
||||||
|
pub fn ok(message: impl Into<String>) -> Status {
|
||||||
|
Status::new(Code::Ok, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The operation was cancelled (typically by the caller).
|
||||||
|
pub fn cancelled(message: impl Into<String>) -> Status {
|
||||||
|
Status::new(Code::Cancelled, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unknown error. An example of where this error may be returned is if a
|
||||||
|
/// `Status` value received from another address space belongs to an error-space
|
||||||
|
/// that is not known in this address space. Also errors raised by APIs that
|
||||||
|
/// do not return enough error information may be converted to this error.
|
||||||
|
pub fn unknown(message: impl Into<String>) -> Status {
|
||||||
|
Status::new(Code::Unknown, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Client specified an invalid argument. Note that this differs from
|
||||||
|
/// `FailedPrecondition`. `InvalidArgument` indicates arguments that are
|
||||||
|
/// problematic regardless of the state of the system (e.g., a malformed file
|
||||||
|
/// name).
|
||||||
|
pub fn invalid_argument(message: impl Into<String>) -> Status {
|
||||||
|
Status::new(Code::InvalidArgument, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deadline expired before operation could complete. For operations that
|
||||||
|
/// change the state of the system, this error may be returned even if the
|
||||||
|
/// operation has completed successfully. For example, a successful response
|
||||||
|
/// from a server could have been delayed long enough for the deadline to
|
||||||
|
/// expire.
|
||||||
|
pub fn deadline_exceeded(message: impl Into<String>) -> Status {
|
||||||
|
Status::new(Code::DeadlineExceeded, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Some requested entity (e.g., file or directory) was not found.
|
||||||
|
pub fn not_found(message: impl Into<String>) -> Status {
|
||||||
|
Status::new(Code::NotFound, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Some entity that we attempted to create (e.g., file or directory) already
|
||||||
|
/// exists.
|
||||||
|
pub fn already_exists(message: impl Into<String>) -> Status {
|
||||||
|
Status::new(Code::AlreadyExists, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The caller does not have permission to execute the specified operation.
|
||||||
|
/// `PermissionDenied` must not be used for rejections caused by exhausting
|
||||||
|
/// some resource (use `ResourceExhausted` instead for those errors).
|
||||||
|
/// `PermissionDenied` must not be used if the caller cannot be identified
|
||||||
|
/// (use `Unauthenticated` instead for those errors).
|
||||||
|
pub fn permission_denied(message: impl Into<String>) -> Status {
|
||||||
|
Status::new(Code::PermissionDenied, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Some resource has been exhausted, perhaps a per-user quota, or perhaps
|
||||||
|
/// the entire file system is out of space.
|
||||||
|
pub fn resource_exhausted(message: impl Into<String>) -> Status {
|
||||||
|
Status::new(Code::ResourceExhausted, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Operation was rejected because the system is not in a state required for
|
||||||
|
/// the operation's execution. For example, directory to be deleted may be
|
||||||
|
/// non-empty, an rmdir operation is applied to a non-directory, etc.
|
||||||
|
///
|
||||||
|
/// A litmus test that may help a service implementor in deciding between
|
||||||
|
/// `FailedPrecondition`, `Aborted`, and `Unavailable`:
|
||||||
|
/// (a) Use `Unavailable` if the client can retry just the failing call.
|
||||||
|
/// (b) Use `Aborted` if the client should retry at a higher-level (e.g.,
|
||||||
|
/// restarting a read-modify-write sequence).
|
||||||
|
/// (c) Use `FailedPrecondition` if the client should not retry until the
|
||||||
|
/// system state has been explicitly fixed. E.g., if an "rmdir" fails
|
||||||
|
/// because the directory is non-empty, `FailedPrecondition` should be
|
||||||
|
/// returned since the client should not retry unless they have first
|
||||||
|
/// fixed up the directory by deleting files from it.
|
||||||
|
pub fn failed_precondition(message: impl Into<String>) -> Status {
|
||||||
|
Status::new(Code::FailedPrecondition, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The operation was aborted, typically due to a concurrency issue like
|
||||||
|
/// sequencer check failures, transaction aborts, etc.
|
||||||
|
///
|
||||||
|
/// See litmus test above for deciding between `FailedPrecondition`,
|
||||||
|
/// `Aborted`, and `Unavailable`.
|
||||||
|
pub fn aborted(message: impl Into<String>) -> Status {
|
||||||
|
Status::new(Code::Aborted, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Operation was attempted past the valid range. E.g., seeking or reading
|
||||||
|
/// past end of file.
|
||||||
|
///
|
||||||
|
/// Unlike `InvalidArgument`, this error indicates a problem that may be
|
||||||
|
/// fixed if the system state changes. For example, a 32-bit file system will
|
||||||
|
/// generate `InvalidArgument if asked to read at an offset that is not in the
|
||||||
|
/// range [0,2^32-1], but it will generate `OutOfRange` if asked to read from
|
||||||
|
/// an offset past the current file size.
|
||||||
|
///
|
||||||
|
/// There is a fair bit of overlap between `FailedPrecondition` and
|
||||||
|
/// `OutOfRange`. We recommend using `OutOfRange` (the more specific error)
|
||||||
|
/// when it applies so that callers who are iterating through a space can
|
||||||
|
/// easily look for an `OutOfRange` error to detect when they are done.
|
||||||
|
pub fn out_of_range(message: impl Into<String>) -> Status {
|
||||||
|
Status::new(Code::OutOfRange, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Operation is not implemented or not supported/enabled in this service.
|
||||||
pub fn unimplemented(message: impl Into<String>) -> Status {
|
pub fn unimplemented(message: impl Into<String>) -> Status {
|
||||||
Status {
|
Status::new(Code::Unimplemented, message)
|
||||||
code: Code::Unimplemented,
|
}
|
||||||
message: message.into(),
|
|
||||||
details: Bytes::new(),
|
/// Internal errors. Means some invariants expected by underlying system has
|
||||||
}
|
/// been broken. If you see one of these errors, something is very broken.
|
||||||
|
pub fn internal(message: impl Into<String>) -> Status {
|
||||||
|
Status::new(Code::Internal, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The service is currently unavailable. This is a most likely a transient
|
||||||
|
/// condition and may be corrected by retrying with a back-off.
|
||||||
|
///
|
||||||
|
/// See litmus test above for deciding between `FailedPrecondition`,
|
||||||
|
/// `Aborted`, and `Unavailable`.
|
||||||
|
pub fn unavailable(message: impl Into<String>) -> Status {
|
||||||
|
Status::new(Code::Unavailable, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unrecoverable data loss or corruption.
|
||||||
|
pub fn data_loss(message: impl Into<String>) -> Status {
|
||||||
|
Status::new(Code::DataLoss, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The request does not have valid authentication credentials for the
|
||||||
|
/// operation.
|
||||||
|
pub fn unauthenticated(message: impl Into<String>) -> Status {
|
||||||
|
Status::new(Code::Unauthenticated, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(not(feature = "h2"), allow(dead_code))]
|
#[cfg_attr(not(feature = "h2"), allow(dead_code))]
|
||||||
@@ -505,4 +675,31 @@ mod tests {
|
|||||||
assert_eq!(Code::from(-1), Code::Unknown);
|
assert_eq!(Code::from(-1), Code::Unknown);
|
||||||
assert_eq!(Code::from(Code::__NonExhaustive as i32), Code::Unknown);
|
assert_eq!(Code::from(Code::__NonExhaustive as i32), Code::Unknown);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn constructors() {
|
||||||
|
assert_eq!(Status::ok("").code(), Code::Ok);
|
||||||
|
assert_eq!(Status::cancelled("").code(), Code::Cancelled);
|
||||||
|
assert_eq!(Status::unknown("").code(), Code::Unknown);
|
||||||
|
assert_eq!(Status::invalid_argument("").code(), Code::InvalidArgument);
|
||||||
|
assert_eq!(Status::deadline_exceeded("").code(), Code::DeadlineExceeded);
|
||||||
|
assert_eq!(Status::not_found("").code(), Code::NotFound);
|
||||||
|
assert_eq!(Status::already_exists("").code(), Code::AlreadyExists);
|
||||||
|
assert_eq!(Status::permission_denied("").code(), Code::PermissionDenied);
|
||||||
|
assert_eq!(
|
||||||
|
Status::resource_exhausted("").code(),
|
||||||
|
Code::ResourceExhausted
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Status::failed_precondition("").code(),
|
||||||
|
Code::FailedPrecondition
|
||||||
|
);
|
||||||
|
assert_eq!(Status::aborted("").code(), Code::Aborted);
|
||||||
|
assert_eq!(Status::out_of_range("").code(), Code::OutOfRange);
|
||||||
|
assert_eq!(Status::unimplemented("").code(), Code::Unimplemented);
|
||||||
|
assert_eq!(Status::internal("").code(), Code::Internal);
|
||||||
|
assert_eq!(Status::unavailable("").code(), Code::Unavailable);
|
||||||
|
assert_eq!(Status::data_loss("").code(), Code::DataLoss);
|
||||||
|
assert_eq!(Status::unauthenticated("").code(), Code::Unauthenticated);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user