blob: 8ed5330ade55635f34ccc3e41c496c4d50996535 [file] [log] [blame]
// Copyright 2020 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#pragma once
#include "pw_status/internal/config.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// This is the pw_Status enum. pw_Status is used to return the status from an
// operation.
//
// In C++, use the pw::Status class instead of the pw_Status enum. pw_Status and
// Status implicitly convert to one another and can be passed cleanly between C
// and C++ APIs.
//
// pw_Status uses the canonical Google error codes. The following enum was based
// on Abseil's status/status.h. The values are all-caps and prefixed with
// PW_STATUS_ instead of using C++ constant style.
typedef enum {
// Ok (gRPC code "OK") does not indicate an error; this value is returned on
// success. It is typical to check for this value before proceeding on any
// given call across an API or RPC boundary. To check this value, use the
// `Status::ok()` member function rather than inspecting the raw code.
PW_STATUS_OK = 0, // Use OkStatus() in C++
// Cancelled (gRPC code "CANCELLED") indicates the operation was cancelled,
// typically by the caller.
PW_STATUS_CANCELLED = 1, // Use Status::Cancelled() in C++
// Unknown (gRPC code "UNKNOWN") indicates an unknown error occurred. In
// general, more specific errors should be raised, if possible. Errors raised
// by APIs that do not return enough error information may be converted to
// this error.
PW_STATUS_UNKNOWN = 2, // Use Status::Unknown() in C++
// InvalidArgument (gRPC code "INVALID_ARGUMENT") indicates the caller
// specified an invalid argument, such a malformed filename. Note that such
// errors should be narrowly limited to indicate to the invalid nature of the
// arguments themselves. Errors with validly formed arguments that may cause
// errors with the state of the receiving system should be denoted with
// `FailedPrecondition` instead.
PW_STATUS_INVALID_ARGUMENT = 3, // Use Status::InvalidArgument() in C++
// DeadlineExceeded (gRPC code "DEADLINE_EXCEEDED") indicates a deadline
// expired before the operation could complete. For operations that may change
// state within a 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.
PW_STATUS_DEADLINE_EXCEEDED = 4, // Use Status::DeadlineExceeded() in C++
// NotFound (gRPC code "NOT_FOUND") indicates some requested entity (such as
// a file or directory) was not found.
//
// `NotFound` is useful if a request should be denied for an entire class of
// users, such as during a gradual feature rollout or undocumented allow list.
// If, instead, a request should be denied for specific sets of users, such as
// through user-based access control, use `PermissionDenied` instead.
PW_STATUS_NOT_FOUND = 5, // Use Status::NotFound() in C++
// AlreadyExists (gRPC code "ALREADY_EXISTS") indicates the entity that a
// caller attempted to create (such as file or directory) is already present.
PW_STATUS_ALREADY_EXISTS = 6, // Use Status::AlreadyExists() in C++
// PermissionDenied (gRPC code "PERMISSION_DENIED") indicates that the caller
// does not have permission to execute the specified operation. Note that this
// error is different than an error due to an *un*authenticated user. This
// error code does not imply the request is valid or the requested entity
// exists or satisfies any other pre-conditions.
//
// `PermissionDenied` must not be used for rejections caused by exhausting
// some resource. Instead, use `ResourceExhausted` for those errors.
// `PermissionDenied` must not be used if the caller cannot be identified.
// Instead, use `Unauthenticated` for those errors.
PW_STATUS_PERMISSION_DENIED = 7, // Use Status::PermissionDenied() in C++
// ResourceExhausted (gRPC code "RESOURCE_EXHAUSTED") indicates some resource
// has been exhausted, perhaps a per-user quota, or perhaps the entire file
// system is out of space.
PW_STATUS_RESOURCE_EXHAUSTED = 8, // Use Status::ResourceExhausted() in C++
// FailedPrecondition (gRPC code "FAILED_PRECONDITION") indicates that the
// operation was rejected because the system is not in a state required for
// the operation's execution. For example, a directory to be deleted may be
// non-empty, an "rmdir" operation is applied to a non-directory, etc.
//
// Some guidelines that may help a service implementer 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 transaction
// level (such as when a client-specified test-and-set fails, indicating
// the client should restart a read-modify-write sequence).
// (c) Use `FailedPrecondition` if the client should not retry until
// the system state has been explicitly fixed. For example, if an "rmdir"
// fails because the directory is non-empty, `FailedPrecondition`
// should be returned since the client should not retry unless
// the files are deleted from the directory.
PW_STATUS_FAILED_PRECONDITION = 9, // Use Status::FailedPrecondition() in C++
// Aborted (gRPC code "ABORTED") indicates the operation was aborted,
// typically due to a concurrency issue such as a sequencer check failure or a
// failed transaction.
//
// See the guidelines above for deciding between `FailedPrecondition`,
// `Aborted`, and `Unavailable`.
PW_STATUS_ABORTED = 10, // Use Status::Aborted() in C++
// OutOfRange (gRPC code "OUT_OF_RANGE") indicates the operation was
// attempted past the valid range, such as seeking or reading past an
// 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.
PW_STATUS_OUT_OF_RANGE = 11, // Use Status::OutOfRange() in C++
// Unimplemented (gRPC code "UNIMPLEMENTED") indicates the operation is not
// implemented or supported in this service. In this case, the operation
// should not be re-attempted.
PW_STATUS_UNIMPLEMENTED = 12, // Use Status::Unimplemented() in C++
// Internal (gRPC code "INTERNAL") indicates an internal error has occurred
// and some invariants expected by the underlying system have not been
// satisfied. This error code is reserved for serious errors.
PW_STATUS_INTERNAL = 13, // Use Status::Internal() in C++
// Unavailable (gRPC code "UNAVAILABLE") indicates the service is currently
// unavailable and that this is most likely a transient condition. An error
// such as this can be corrected by retrying with a backoff scheme. Note that
// it is not always safe to retry non-idempotent operations.
//
// See the guidelines above for deciding between `FailedPrecondition`,
// `Aborted`, and `Unavailable`.
PW_STATUS_UNAVAILABLE = 14, // Use Status::Unavailable() in C++
// DataLoss (gRPC code "DATA_LOSS") indicates that unrecoverable data loss or
// corruption has occurred. As this error is serious, proper alerting should
// be attached to errors such as this.
PW_STATUS_DATA_LOSS = 15, // Use Status::DataLoss() in C++
// Unauthenticated (gRPC code "UNAUTHENTICATED") indicates that the request
// does not have valid authentication credentials for the operation. Correct
// the authentication and try again.
PW_STATUS_UNAUTHENTICATED = 16, // Use Status::Unauthenticated() in C++
// NOTE: this error code entry should not be used and you should not rely on
// its value, which may change.
//
// The purpose of this enumerated value is to force people who handle status
// codes with `switch()` statements to *not* simply enumerate all possible
// values, but instead provide a "default:" case. Providing such a default
// case ensures that code will compile when new codes are added.
PW_STATUS_DO_NOT_USE_RESERVED_FOR_FUTURE_EXPANSION_USE_DEFAULT_IN_SWITCH_INSTEAD_,
} pw_Status; // Use pw::Status in C++
// Returns a null-terminated string representation of the pw_Status.
const char* pw_StatusString(pw_Status status);
#ifdef __cplusplus
} // extern "C"
namespace pw {
// The Status class is a thin, zero-cost abstraction around the pw_Status enum.
// It initializes to OkStatus() by default and adds ok() and str() methods.
// Implicit conversions are permitted between pw_Status and pw::Status.
class _PW_STATUS_NO_DISCARD Status {
public:
using Code = pw_Status;
// Functions that create a Status with the specified code.
// clang-format off
[[nodiscard]] static constexpr Status Cancelled() {
return PW_STATUS_CANCELLED;
}
[[nodiscard]] static constexpr Status Unknown() {
return PW_STATUS_UNKNOWN;
}
[[nodiscard]] static constexpr Status InvalidArgument() {
return PW_STATUS_INVALID_ARGUMENT;
}
[[nodiscard]] static constexpr Status DeadlineExceeded() {
return PW_STATUS_DEADLINE_EXCEEDED;
}
[[nodiscard]] static constexpr Status NotFound() {
return PW_STATUS_NOT_FOUND;
}
[[nodiscard]] static constexpr Status AlreadyExists() {
return PW_STATUS_ALREADY_EXISTS;
}
[[nodiscard]] static constexpr Status PermissionDenied() {
return PW_STATUS_PERMISSION_DENIED;
}
[[nodiscard]] static constexpr Status ResourceExhausted() {
return PW_STATUS_RESOURCE_EXHAUSTED;
}
[[nodiscard]] static constexpr Status FailedPrecondition() {
return PW_STATUS_FAILED_PRECONDITION;
}
[[nodiscard]] static constexpr Status Aborted() {
return PW_STATUS_ABORTED;
}
[[nodiscard]] static constexpr Status OutOfRange() {
return PW_STATUS_OUT_OF_RANGE;
}
[[nodiscard]] static constexpr Status Unimplemented() {
return PW_STATUS_UNIMPLEMENTED;
}
[[nodiscard]] static constexpr Status Internal() {
return PW_STATUS_INTERNAL;
}
[[nodiscard]] static constexpr Status Unavailable() {
return PW_STATUS_UNAVAILABLE;
}
[[nodiscard]] static constexpr Status DataLoss() {
return PW_STATUS_DATA_LOSS;
}
[[nodiscard]] static constexpr Status Unauthenticated() {
return PW_STATUS_UNAUTHENTICATED;
}
// clang-format on
// Statuses are created with a Status::Code.
constexpr Status(Code code = PW_STATUS_OK) : code_(code) {}
constexpr Status(const Status&) = default;
constexpr Status& operator=(const Status&) = default;
// Returns the Status::Code (pw_Status) for this Status.
constexpr Code code() const { return code_; }
// True if the status is OK.
[[nodiscard]] constexpr bool ok() const { return code_ == PW_STATUS_OK; }
// Functions for checking which status this is.
[[nodiscard]] constexpr bool IsCancelled() const {
return code_ == PW_STATUS_CANCELLED;
}
[[nodiscard]] constexpr bool IsUnknown() const {
return code_ == PW_STATUS_UNKNOWN;
}
[[nodiscard]] constexpr bool IsInvalidArgument() const {
return code_ == PW_STATUS_INVALID_ARGUMENT;
}
[[nodiscard]] constexpr bool IsDeadlineExceeded() const {
return code_ == PW_STATUS_DEADLINE_EXCEEDED;
}
[[nodiscard]] constexpr bool IsNotFound() const {
return code_ == PW_STATUS_NOT_FOUND;
}
[[nodiscard]] constexpr bool IsAlreadyExists() const {
return code_ == PW_STATUS_ALREADY_EXISTS;
}
[[nodiscard]] constexpr bool IsPermissionDenied() const {
return code_ == PW_STATUS_PERMISSION_DENIED;
}
[[nodiscard]] constexpr bool IsResourceExhausted() const {
return code_ == PW_STATUS_RESOURCE_EXHAUSTED;
}
[[nodiscard]] constexpr bool IsFailedPrecondition() const {
return code_ == PW_STATUS_FAILED_PRECONDITION;
}
[[nodiscard]] constexpr bool IsAborted() const {
return code_ == PW_STATUS_ABORTED;
}
[[nodiscard]] constexpr bool IsOutOfRange() const {
return code_ == PW_STATUS_OUT_OF_RANGE;
}
[[nodiscard]] constexpr bool IsUnimplemented() const {
return code_ == PW_STATUS_UNIMPLEMENTED;
}
[[nodiscard]] constexpr bool IsInternal() const {
return code_ == PW_STATUS_INTERNAL;
}
[[nodiscard]] constexpr bool IsUnavailable() const {
return code_ == PW_STATUS_UNAVAILABLE;
}
[[nodiscard]] constexpr bool IsDataLoss() const {
return code_ == PW_STATUS_DATA_LOSS;
}
[[nodiscard]] constexpr bool IsUnauthenticated() const {
return code_ == PW_STATUS_UNAUTHENTICATED;
}
// Updates this Status to the provided Status IF this status is OK. This is
// useful for tracking the first encountered error, as calls to this helper
// will not change one error status to another error status.
constexpr void Update(Status other) {
if (ok()) {
code_ = other.code();
}
}
// Ignores any errors. This method does nothing except potentially suppress
// complaints from any tools that are checking that errors are not dropped on
// the floor.
constexpr void IgnoreError() const {}
// Returns a null-terminated string representation of the Status.
[[nodiscard]] const char* str() const { return pw_StatusString(code_); }
private:
Code code_;
};
// Returns an OK status. Equivalent to Status() or Status(PW_STATUS_OK). This
// function is used instead of a Status::Ok() function, which would be too
// similar to Status::ok().
[[nodiscard]] constexpr Status OkStatus() { return Status(); }
constexpr bool operator==(const Status& lhs, const Status& rhs) {
return lhs.code() == rhs.code();
}
constexpr bool operator!=(const Status& lhs, const Status& rhs) {
return lhs.code() != rhs.code();
}
} // namespace pw
// Create a C++ overload of pw_StatusString so that it supports pw::Status in
// addition to pw_Status.
inline const char* pw_StatusString(pw::Status status) {
return pw_StatusString(status.code());
}
#endif // __cplusplus