• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #pragma once
15 
16 #include "pw_status/internal/config.h"
17 
18 #ifdef __cplusplus
19 extern "C" {
20 #endif  // __cplusplus
21 
22 // This is the pw_Status enum. pw_Status is used to return the status from an
23 // operation.
24 //
25 // In C++, use the pw::Status class instead of the pw_Status enum. pw_Status and
26 // Status implicitly convert to one another and can be passed cleanly between C
27 // and C++ APIs.
28 //
29 // pw_Status uses the canonical Google error codes. The following enum was based
30 // on Abseil's status/status.h. The values are all-caps and prefixed with
31 // PW_STATUS_ instead of using C++ constant style.
32 typedef enum {
33   // Ok (gRPC code "OK") does not indicate an error; this value is returned on
34   // success. It is typical to check for this value before proceeding on any
35   // given call across an API or RPC boundary. To check this value, use the
36   // `Status::ok()` member function rather than inspecting the raw code.
37   PW_STATUS_OK = 0,  // Use OkStatus() in C++
38 
39   // Cancelled (gRPC code "CANCELLED") indicates the operation was cancelled,
40   // typically by the caller.
41   PW_STATUS_CANCELLED = 1,  // Use Status::Cancelled() in C++
42 
43   // Unknown (gRPC code "UNKNOWN") indicates an unknown error occurred. In
44   // general, more specific errors should be raised, if possible. Errors raised
45   // by APIs that do not return enough error information may be converted to
46   // this error.
47   PW_STATUS_UNKNOWN = 2,  // Use Status::Unknown() in C++
48 
49   // InvalidArgument (gRPC code "INVALID_ARGUMENT") indicates the caller
50   // specified an invalid argument, such a malformed filename. Note that such
51   // errors should be narrowly limited to indicate to the invalid nature of the
52   // arguments themselves. Errors with validly formed arguments that may cause
53   // errors with the state of the receiving system should be denoted with
54   // `FailedPrecondition` instead.
55   PW_STATUS_INVALID_ARGUMENT = 3,  // Use Status::InvalidArgument() in C++
56 
57   // DeadlineExceeded (gRPC code "DEADLINE_EXCEEDED") indicates a deadline
58   // expired before the operation could complete. For operations that may change
59   // state within a system, this error may be returned even if the operation has
60   // completed successfully. For example, a successful response from a server
61   // could have been delayed long enough for the deadline to expire.
62   PW_STATUS_DEADLINE_EXCEEDED = 4,  // Use Status::DeadlineExceeded() in C++
63 
64   // NotFound (gRPC code "NOT_FOUND") indicates some requested entity (such as
65   // a file or directory) was not found.
66   //
67   // `NotFound` is useful if a request should be denied for an entire class of
68   // users, such as during a gradual feature rollout or undocumented allow list.
69   // If, instead, a request should be denied for specific sets of users, such as
70   // through user-based access control, use `PermissionDenied` instead.
71   PW_STATUS_NOT_FOUND = 5,  // Use Status::NotFound() in C++
72 
73   // AlreadyExists (gRPC code "ALREADY_EXISTS") indicates the entity that a
74   // caller attempted to create (such as file or directory) is already present.
75   PW_STATUS_ALREADY_EXISTS = 6,  // Use Status::AlreadyExists() in C++
76 
77   // PermissionDenied (gRPC code "PERMISSION_DENIED") indicates that the caller
78   // does not have permission to execute the specified operation. Note that this
79   // error is different than an error due to an *un*authenticated user. This
80   // error code does not imply the request is valid or the requested entity
81   // exists or satisfies any other pre-conditions.
82   //
83   // `PermissionDenied` must not be used for rejections caused by exhausting
84   // some resource. Instead, use `ResourceExhausted` for those errors.
85   // `PermissionDenied` must not be used if the caller cannot be identified.
86   // Instead, use `Unauthenticated` for those errors.
87   PW_STATUS_PERMISSION_DENIED = 7,  // Use Status::PermissionDenied() in C++
88 
89   // ResourceExhausted (gRPC code "RESOURCE_EXHAUSTED") indicates some resource
90   // has been exhausted, perhaps a per-user quota, or perhaps the entire file
91   // system is out of space.
92   PW_STATUS_RESOURCE_EXHAUSTED = 8,  // Use Status::ResourceExhausted() in C++
93 
94   // FailedPrecondition (gRPC code "FAILED_PRECONDITION") indicates that the
95   // operation was rejected because the system is not in a state required for
96   // the operation's execution. For example, a directory to be deleted may be
97   // non-empty, an "rmdir" operation is applied to a non-directory, etc.
98   //
99   // Some guidelines that may help a service implementer in deciding between
100   // `FailedPrecondition`, `Aborted`, and `Unavailable`:
101   //
102   //  (a) Use `Unavailable` if the client can retry just the failing call.
103   //  (b) Use `Aborted` if the client should retry at a higher transaction
104   //      level (such as when a client-specified test-and-set fails, indicating
105   //      the client should restart a read-modify-write sequence).
106   //  (c) Use `FailedPrecondition` if the client should not retry until
107   //      the system state has been explicitly fixed. For example, if an "rmdir"
108   //      fails because the directory is non-empty, `FailedPrecondition`
109   //      should be returned since the client should not retry unless
110   //      the files are deleted from the directory.
111   PW_STATUS_FAILED_PRECONDITION = 9,  // Use Status::FailedPrecondition() in C++
112 
113   // Aborted (gRPC code "ABORTED") indicates the operation was aborted,
114   // typically due to a concurrency issue such as a sequencer check failure or a
115   // failed transaction.
116   //
117   // See the guidelines above for deciding between `FailedPrecondition`,
118   // `Aborted`, and `Unavailable`.
119   PW_STATUS_ABORTED = 10,  // Use Status::Aborted() in C++
120 
121   // OutOfRange (gRPC code "OUT_OF_RANGE") indicates the operation was
122   // attempted past the valid range, such as seeking or reading past an
123   // end-of-file.
124   //
125   // Unlike `InvalidArgument`, this error indicates a problem that may
126   // be fixed if the system state changes. For example, a 32-bit file
127   // system will generate `InvalidArgument` if asked to read at an
128   // offset that is not in the range [0,2^32-1], but it will generate
129   // `OutOfRange` if asked to read from an offset past the current
130   // file size.
131   //
132   // There is a fair bit of overlap between `FailedPrecondition` and
133   // `OutOfRange`.  We recommend using `OutOfRange` (the more specific
134   // error) when it applies so that callers who are iterating through
135   // a space can easily look for an `OutOfRange` error to detect when
136   // they are done.
137   PW_STATUS_OUT_OF_RANGE = 11,  // Use Status::OutOfRange() in C++
138 
139   // Unimplemented (gRPC code "UNIMPLEMENTED") indicates the operation is not
140   // implemented or supported in this service. In this case, the operation
141   // should not be re-attempted.
142   PW_STATUS_UNIMPLEMENTED = 12,  // Use Status::Unimplemented() in C++
143 
144   // Internal (gRPC code "INTERNAL") indicates an internal error has occurred
145   // and some invariants expected by the underlying system have not been
146   // satisfied. This error code is reserved for serious errors.
147   PW_STATUS_INTERNAL = 13,  // Use Status::Internal() in C++
148 
149   // Unavailable (gRPC code "UNAVAILABLE") indicates the service is currently
150   // unavailable and that this is most likely a transient condition. An error
151   // such as this can be corrected by retrying with a backoff scheme. Note that
152   // it is not always safe to retry non-idempotent operations.
153   //
154   // See the guidelines above for deciding between `FailedPrecondition`,
155   // `Aborted`, and `Unavailable`.
156   PW_STATUS_UNAVAILABLE = 14,  // Use Status::Unavailable() in C++
157 
158   // DataLoss (gRPC code "DATA_LOSS") indicates that unrecoverable data loss or
159   // corruption has occurred. As this error is serious, proper alerting should
160   // be attached to errors such as this.
161   PW_STATUS_DATA_LOSS = 15,  // Use Status::DataLoss() in C++
162 
163   // Unauthenticated (gRPC code "UNAUTHENTICATED") indicates that the request
164   // does not have valid authentication credentials for the operation. Correct
165   // the authentication and try again.
166   PW_STATUS_UNAUTHENTICATED = 16,  // Use Status::Unauthenticated() in C++
167 
168   // NOTE: this error code entry should not be used and you should not rely on
169   // its value, which may change.
170   //
171   // The purpose of this enumerated value is to force people who handle status
172   // codes with `switch()` statements to *not* simply enumerate all possible
173   // values, but instead provide a "default:" case. Providing such a default
174   // case ensures that code will compile when new codes are added.
175   PW_STATUS_DO_NOT_USE_RESERVED_FOR_FUTURE_EXPANSION_USE_DEFAULT_IN_SWITCH_INSTEAD_,
176 } pw_Status;  // Use pw::Status in C++
177 
178 // Returns a null-terminated string representation of the pw_Status.
179 const char* pw_StatusString(pw_Status status);
180 
181 #ifdef __cplusplus
182 
183 }  // extern "C"
184 
185 namespace pw {
186 
187 // The Status class is a thin, zero-cost abstraction around the pw_Status enum.
188 // It initializes to OkStatus() by default and adds ok() and str() methods.
189 // Implicit conversions are permitted between pw_Status and pw::Status.
190 class _PW_STATUS_NO_DISCARD Status {
191  public:
192   using Code = pw_Status;
193 
194   // Functions that create a Status with the specified code.
195   // clang-format off
Cancelled()196   [[nodiscard]] static constexpr Status Cancelled() {
197     return PW_STATUS_CANCELLED;
198   }
Unknown()199   [[nodiscard]] static constexpr Status Unknown() {
200     return PW_STATUS_UNKNOWN;
201   }
InvalidArgument()202   [[nodiscard]] static constexpr Status InvalidArgument() {
203     return PW_STATUS_INVALID_ARGUMENT;
204   }
DeadlineExceeded()205   [[nodiscard]] static constexpr Status DeadlineExceeded() {
206     return PW_STATUS_DEADLINE_EXCEEDED;
207   }
NotFound()208   [[nodiscard]] static constexpr Status NotFound() {
209     return PW_STATUS_NOT_FOUND;
210   }
AlreadyExists()211   [[nodiscard]] static constexpr Status AlreadyExists() {
212     return PW_STATUS_ALREADY_EXISTS;
213   }
PermissionDenied()214   [[nodiscard]] static constexpr Status PermissionDenied() {
215     return PW_STATUS_PERMISSION_DENIED;
216   }
ResourceExhausted()217   [[nodiscard]] static constexpr Status ResourceExhausted() {
218     return PW_STATUS_RESOURCE_EXHAUSTED;
219   }
FailedPrecondition()220   [[nodiscard]] static constexpr Status FailedPrecondition() {
221     return PW_STATUS_FAILED_PRECONDITION;
222   }
Aborted()223   [[nodiscard]] static constexpr Status Aborted() {
224     return PW_STATUS_ABORTED;
225   }
OutOfRange()226   [[nodiscard]] static constexpr Status OutOfRange() {
227     return PW_STATUS_OUT_OF_RANGE;
228   }
Unimplemented()229   [[nodiscard]] static constexpr Status Unimplemented() {
230     return PW_STATUS_UNIMPLEMENTED;
231   }
Internal()232   [[nodiscard]] static constexpr Status Internal() {
233     return PW_STATUS_INTERNAL;
234   }
Unavailable()235   [[nodiscard]] static constexpr Status Unavailable() {
236     return PW_STATUS_UNAVAILABLE;
237   }
DataLoss()238   [[nodiscard]] static constexpr Status DataLoss() {
239     return PW_STATUS_DATA_LOSS;
240   }
Unauthenticated()241   [[nodiscard]] static constexpr Status Unauthenticated() {
242     return PW_STATUS_UNAUTHENTICATED;
243   }
244   // clang-format on
245 
246   // Statuses are created with a Status::Code.
code_(code)247   constexpr Status(Code code = PW_STATUS_OK) : code_(code) {}
248 
249   constexpr Status(const Status&) = default;
250   constexpr Status& operator=(const Status&) = default;
251 
252   // Returns the Status::Code (pw_Status) for this Status.
code()253   constexpr Code code() const { return code_; }
254 
255   // True if the status is OK.
ok()256   [[nodiscard]] constexpr bool ok() const { return code_ == PW_STATUS_OK; }
257 
258   // Functions for checking which status this is.
IsCancelled()259   [[nodiscard]] constexpr bool IsCancelled() const {
260     return code_ == PW_STATUS_CANCELLED;
261   }
IsUnknown()262   [[nodiscard]] constexpr bool IsUnknown() const {
263     return code_ == PW_STATUS_UNKNOWN;
264   }
IsInvalidArgument()265   [[nodiscard]] constexpr bool IsInvalidArgument() const {
266     return code_ == PW_STATUS_INVALID_ARGUMENT;
267   }
IsDeadlineExceeded()268   [[nodiscard]] constexpr bool IsDeadlineExceeded() const {
269     return code_ == PW_STATUS_DEADLINE_EXCEEDED;
270   }
IsNotFound()271   [[nodiscard]] constexpr bool IsNotFound() const {
272     return code_ == PW_STATUS_NOT_FOUND;
273   }
IsAlreadyExists()274   [[nodiscard]] constexpr bool IsAlreadyExists() const {
275     return code_ == PW_STATUS_ALREADY_EXISTS;
276   }
IsPermissionDenied()277   [[nodiscard]] constexpr bool IsPermissionDenied() const {
278     return code_ == PW_STATUS_PERMISSION_DENIED;
279   }
IsResourceExhausted()280   [[nodiscard]] constexpr bool IsResourceExhausted() const {
281     return code_ == PW_STATUS_RESOURCE_EXHAUSTED;
282   }
IsFailedPrecondition()283   [[nodiscard]] constexpr bool IsFailedPrecondition() const {
284     return code_ == PW_STATUS_FAILED_PRECONDITION;
285   }
IsAborted()286   [[nodiscard]] constexpr bool IsAborted() const {
287     return code_ == PW_STATUS_ABORTED;
288   }
IsOutOfRange()289   [[nodiscard]] constexpr bool IsOutOfRange() const {
290     return code_ == PW_STATUS_OUT_OF_RANGE;
291   }
IsUnimplemented()292   [[nodiscard]] constexpr bool IsUnimplemented() const {
293     return code_ == PW_STATUS_UNIMPLEMENTED;
294   }
IsInternal()295   [[nodiscard]] constexpr bool IsInternal() const {
296     return code_ == PW_STATUS_INTERNAL;
297   }
IsUnavailable()298   [[nodiscard]] constexpr bool IsUnavailable() const {
299     return code_ == PW_STATUS_UNAVAILABLE;
300   }
IsDataLoss()301   [[nodiscard]] constexpr bool IsDataLoss() const {
302     return code_ == PW_STATUS_DATA_LOSS;
303   }
IsUnauthenticated()304   [[nodiscard]] constexpr bool IsUnauthenticated() const {
305     return code_ == PW_STATUS_UNAUTHENTICATED;
306   }
307 
308   // Updates this Status to the provided Status IF this status is OK. This is
309   // useful for tracking the first encountered error, as calls to this helper
310   // will not change one error status to another error status.
Update(Status other)311   constexpr void Update(Status other) {
312     if (ok()) {
313       code_ = other.code();
314     }
315   }
316 
317   // Ignores any errors. This method does nothing except potentially suppress
318   // complaints from any tools that are checking that errors are not dropped on
319   // the floor.
IgnoreError()320   constexpr void IgnoreError() const {}
321 
322   // Returns a null-terminated string representation of the Status.
str()323   [[nodiscard]] const char* str() const { return pw_StatusString(code_); }
324 
325  private:
326   Code code_;
327 };
328 
329 // Returns an OK status. Equivalent to Status() or Status(PW_STATUS_OK). This
330 // function is used instead of a Status::Ok() function, which would be too
331 // similar to Status::ok().
OkStatus()332 [[nodiscard]] constexpr Status OkStatus() { return Status(); }
333 
334 constexpr bool operator==(const Status& lhs, const Status& rhs) {
335   return lhs.code() == rhs.code();
336 }
337 
338 constexpr bool operator!=(const Status& lhs, const Status& rhs) {
339   return lhs.code() != rhs.code();
340 }
341 
342 }  // namespace pw
343 
344 // Create a C++ overload of pw_StatusString so that it supports pw::Status in
345 // addition to pw_Status.
pw_StatusString(pw::Status status)346 inline const char* pw_StatusString(pw::Status status) {
347   return pw_StatusString(status.code());
348 }
349 
350 #endif  // __cplusplus
351