diff --git a/pw_status/BUILD.gn b/pw_status/BUILD.gn index 157472b8bf..7ae663926b 100644 --- a/pw_status/BUILD.gn +++ b/pw_status/BUILD.gn @@ -82,5 +82,9 @@ pw_test("try_test") { } pw_doc_group("docs") { - sources = [ "docs.rst" ] + sources = [ + "docs.rst", + "guide.rst", + "reference.rst", + ] } diff --git a/pw_status/docs.rst b/pw_status/docs.rst index 3432361f4f..e95e59dbc7 100644 --- a/pw_status/docs.rst +++ b/pw_status/docs.rst @@ -3,516 +3,144 @@ ========= pw_status ========= -``pw_status`` provides features for communicating the result of an operation. -The classes in ``pw_status`` are used extensively throughout Pigweed. - ------- -Status ------- -The primary feature of ``pw_status`` is the ``Status`` class (in C++) or enum -(in other languages). ``pw_status`` provides an implementation of status in -every supported Pigweed language. - -The C++ implementation is :cpp:class:`pw::Status`, a simple, zero-overhead -status object that wraps a status code. Other languages use an enum. - -Pigweed's status uses Google's standard status codes (see the `Google APIs -repository `_). +.. pigweed-module:: + :name: pw_status + :tagline: Exception-free error propagation for embedded + :status: stable + :languages: C++17, C, Python, Java, TypeScript, Rust + + - **Easy**: Simple to understand, includes convenient macro ``PW_TRY`` + - **Efficient**: No memory allocation, no exceptions + - **Established**: Just like ``absl::Status``, deployed extensively at Google + + :cpp:type:`pw::Status` is Pigweed's error propagation primitive, enabling + exception-free error handling. The primary feature of ``pw_status`` is the + :cpp:class:`pw::Status` class, a simple, zero-overhead status object that + wraps a status code, and the ``PW_TRY`` macro. For example: + + .. code-block:: cpp + + #include "pw_status/status.h" + + pw::Status ImuEnable() { + if (!device_has_imu) { + return Status::FailedPrecondition(); + } + PW_TRY(ImuSpiSendEnable()); // Propagates failure on non-OK status. + return pw::OkStatus(); + } + + void Initialize() { + if (auto status = ImuEnable(); status.ok()) { + PW_LOG_INFO("Imu initialized successfully") + } else { + if (status.IsFailedPrecondition()) { + PW_LOG_WARNING("No IMU present"); + } else { + PW_LOG_ERROR("Unknown error initializing IMU: %d", status.code()); + } + } + } + +``pw_status`` provides an implementation of status in every supported +Pigweed language, including C, Rust, TypeScript, Java, and Python. + +Pigweed's status matches Google's standard status codes (see the `Google APIs +repository +`_). These codes are used extensively in Google projects including `Abseil `_ (`status/status.h `_) and `gRPC `_ (`doc/statuscodes.md `_). -Status codes -============ -.. c:enumerator:: OK = 0 - - ``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 ``ok()`` member function - rather than inspecting the raw code. - - .. list-table:: - - * - C++ - - ``pw::OkStatus()`` - * - C - - ``PW_STATUS_OK`` - * - Python / Java / TypeScript - - ``Status.OK`` - * - Rust - - ``Ok(val)`` - -.. c:enumerator:: CANCELLED = 1 - - ``CANCELLED`` indicates the operation was cancelled, typically by the caller. - - .. list-table:: - - * - C++ - - ``pw::Status::Cancelled()`` - * - C - - ``PW_STATUS_CANCELLED`` - * - Python / Java / TypeScript - - ``Status.CANCELLED`` - * - Rust - - ``Error::Cancelled`` - -.. c:enumerator:: UNKNOWN = 2 - - ``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. - - .. list-table:: - - * - C++ - - ``pw::Status::Unknown()`` - * - C - - ``PW_STATUS_UNKNOWN`` - * - Python / Java / TypeScript - - ``Status.UNKNOWN`` - * - Rust - - ``Error::Unknown`` - -.. c:enumerator:: INVALID_ARGUMENT = 3 - - ``INVALID_ARGUMENT`` indicates the caller specified an invalid argument, such - as a malformed filename. Note that use of such errors should be narrowly - limited to indicate 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 :c:enumerator:`FAILED_PRECONDITION` - instead. - - .. list-table:: - - * - C++ - - ``pw::Status::InvalidArgument()`` - * - C - - ``PW_STATUS_INVALID_ARGUMENT`` - * - Python / Java / TypeScript - - ``Status.INVALID_ARGUMENT`` - * - Rust - - ``Error::InvalidArgument`` - -.. c:enumerator:: DEADLINE_EXCEEDED = 4 - - ``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. - - .. list-table:: - - * - C++ - - ``pw::Status::DeadlineExceeded()`` - * - C - - ``PW_STATUS_DEADLINE_EXCEEDED`` - * - Python / Java / TypeScript - - ``Status.DEADLINE_EXCEEDED`` - * - Rust - - ``Error::DeadlineExceeded`` - -.. c:enumerator:: NOT_FOUND = 5 - - ``NOT_FOUND`` indicates some requested entity (such as a file or directory) - was not found. - - :c:enumerator:`NOT_FOUND` is useful if a request should be denied for an - entire class of users, such as during a gradual feature rollout or - undocumented allowlist. If a request should be denied for specific sets of - users, such as through user-based access control, use - :c:enumerator:`PERMISSION_DENIED` instead. - - .. list-table:: - - * - C++ - - ``pw::Status::NotFound()`` - * - C - - ``PW_STATUS_NOT_FOUND`` - * - Python / Java / TypeScript - - ``Status.NOT_FOUND`` - * - Rust - - ``Error::NotFound`` - -.. c:enumerator:: ALREADY_EXISTS = 6 - - ``ALREADY_EXISTS`` indicates that the entity a caller attempted to create - (such as a file or directory) is already present. - - .. list-table:: - - * - C++ - - ``pw::Status::AlreadyExists()`` - * - C - - ``PW_STATUS_ALREADY_EXISTS`` - * - Python / Java / TypeScript - - ``Status.ALREADY_EXISTS`` - * - Rust - - ``Error::AlreadyExists`` - -.. c:enumerator:: PERMISSION_DENIED = 7 - - ``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 unauthenticated user. This error code does not imply the - request is valid or the requested entity exists or satisfies any other - pre-conditions. - - :c:enumerator:`PERMISSION_DENIED` must not be used for rejections caused by - exhausting some resource. Instead, use :c:enumerator:`RESOURCE_EXHAUSTED` for - those errors. :c:enumerator:`PERMISSION_DENIED` must not be used if the - caller cannot be identified. Instead, use :c:enumerator:`UNAUTHENTICATED` - for those errors. - - .. list-table:: - - * - C++ - - ``pw::Status::PermissionDenied()`` - * - C - - ``PW_STATUS_PERMISSION_DENIED`` - * - Python / Java / TypeScript - - ``Status.PERMISSION_DENIED`` - * - Rust - - ``Error::PermissionDenied`` - -.. c:enumerator:: RESOURCE_EXHAUSTED = 8 - - ``RESOURCE_EXHAUSTED`` indicates some resource has been exhausted, perhaps a - per-user quota, or perhaps the entire file system is out of space. - - .. list-table:: - - * - C++ - - ``pw::Status::ResourceExhausted()`` - * - C - - ``PW_STATUS_RESOURCE_EXHAUSTED`` - * - Python / Java / TypeScript - - ``Status.RESOURCE_EXHAUSTED`` - * - Rust - - ``Error::ResourceExhausted`` - -.. c:enumerator:: FAILED_PRECONDITION = 9 - - ``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. - - .. _module-pw_status-guidelines: - - Some guidelines that may help a service implementer in deciding between - :c:enumerator:`FAILED_PRECONDITION`, :c:enumerator:`ABORTED`, and - :c:enumerator:`UNAVAILABLE`: - - a. Use :c:enumerator:`UNAVAILABLE` if the client can retry just the failing - call. - b. Use :c:enumerator:`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 :c:enumerator:`FAILED_PRECONDITION` if the client should not retry - until the system state has been explicitly fixed. For example, if a - ``rmdir`` fails because the directory is non-empty, - :c:enumerator:`FAILED_PRECONDITION` should be returned since the client - should not retry unless the files are deleted from the directory. - - .. list-table:: - - * - C++ - - ``pw::Status::FailedPrecondition()`` - * - C - - ``PW_STATUS_FAILED_PRECONDITION`` - * - Python / Java / TypeScript - - ``Status.FAILED_PRECONDITION`` - * - Rust - - ``Error::FailedPrecondition`` - -.. c:enumerator:: ABORTED = 10 - - ``ABORTED`` indicates the operation was aborted, typically due to a - concurrency issue such as a sequencer check failure or a failed transaction. - - See the :ref:`guidelines ` above for deciding - between :c:enumerator:`FAILED_PRECONDITION`, :c:enumerator:`ABORTED`, and - :c:enumerator:`UNAVAILABLE`. - - .. list-table:: - - * - C++ - - ``pw::Status::Aborted()`` - * - C - - ``PW_STATUS_ABORTED`` - * - Python / Java / TypeScript - - ``Status.ABORTED`` - * - Rust - - ``Error::Aborted`` - -.. c:enumerator:: OUT_OF_RANGE = 11 - - ``OUT_OF_RANGE`` indicates the operation was attempted past the valid range, - such as seeking or reading past an end-of-file. - - Unlike :c:enumerator:`INVALID_ARGUMENT`, this error indicates a problem that - may be fixed if the system state changes. For example, a 32-bit file system - will generate :c:enumerator:`INVALID_ARGUMENT` if asked to read at an offset - that is not in the range [0,2^32-1], but it will generate - :c:enumerator:`OUT_OF_RANGE` if asked to read from an offset past the current - file size. +.. grid:: 2 - There is a fair bit of overlap between :c:enumerator:`FAILED_PRECONDITION` - and :c:enumerator:`OUT_OF_RANGE`. We recommend using - :c:enumerator:`OUT_OF_RANGE` (the more specific error) when it applies so - that callers who are iterating through a space can easily look for an - :c:enumerator:`OUT_OF_RANGE` error to detect when they are done. + .. grid-item-card:: :octicon:`rocket` Get Started & Guides + :link: module-pw_status-get-started + :link-type: ref + :class-item: sales-pitch-cta-primary - .. list-table:: + Integrate pw_status into your project, see common uses - * - C++ - - ``pw::Status::OutOfRange()`` - * - C - - ``PW_STATUS_OUT_OF_RANGE`` - * - Python / Java / TypeScript - - ``Status.OUT_OF_RANGE`` - * - Rust - - ``Error::OutOfRange`` + .. grid-item-card:: :octicon:`code-square` API Reference + :link: module-pw_status-reference + :link-type: ref + :class-item: sales-pitch-cta-secondary -.. c:enumerator:: UNIMPLEMENTED = 12 + Detailed description of pw_status's methods. - ``UNIMPLEMENTED`` indicates the operation is not implemented or supported in - this service. In this case, the operation should not be re-attempted. - - .. list-table:: - - * - C++ - - ``pw::Status::Unimplemented()`` - * - C - - ``PW_STATUS_UNIMPLEMENTED`` - * - Python / Java / TypeScript - - ``Status.UNIMPLEMENTED`` - * - Rust - - ``Error::Unimplemented`` - -.. c:enumerator:: INTERNAL = 13 - - ``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. - - .. list-table:: - - * - C++ - - ``pw::Status::Internal()`` - * - C - - ``PW_STATUS_INTERNAL`` - * - Python / Java / TypeScript - - ``Status.INTERNAL`` - * - Rust - - ``Error::Internal`` - -.. c:enumerator:: UNAVAILABLE = 14 - - ``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 :ref:`guidelines ` above for deciding - between :c:enumerator:`FAILED_PRECONDITION`, :c:enumerator:`ABORTED`, and - :c:enumerator:`UNAVAILABLE`. - - .. list-table:: - - * - C++ - - ``pw::Status::Unavailable()`` - * - C - - ``PW_STATUS_UNAVAILABLE`` - * - Python / Java / TypeScript - - ``Status.UNAVAILABLE`` - * - Rust - - ``Error::Unavailable`` - -.. c:enumerator:: DATA_LOSS = 15 - - ``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. - - .. list-table:: - - * - C++ - - ``pw::Status::DataLoss()`` - * - C - - ``PW_STATUS_DATA_LOSS`` - * - Python / Java / TypeScript - - ``Status.DATA_LOSS`` - * - Rust - - ``Error::DataLoss`` - -.. c:enumerator:: UNAUTHENTICATED = 16 - - ``UNAUTHENTICATED`` indicates that the request does not have valid - authentication credentials for the operation. Correct the authentication and - try again. - - .. list-table:: - - * - C++ - - ``pw::Status::Unauthenticated()`` - * - C - - ``PW_STATUS_UNAUTHENTICATED`` - * - Python / Java / TypeScript - - ``Status.UNAUTHENTICATED`` - * - Rust - - ``Error::Unauthenticated`` - -C++ API -======= -.. doxygenclass:: pw::Status - :members: - -.. doxygenfunction:: pw::OkStatus - -.. c:enum:: pw_Status - - Enum to use in place of :cpp:class:`pw::Status` in C code. Always use - :cpp:class:`pw::Status` in C++ code. - - The values of the :c:enum:`pw_Status` enum are all-caps and prefixed with - ``PW_STATUS_``. For example, ``PW_STATUS_DATA_LOSS`` corresponds with - :c:enumerator:`DATA_LOSS`. - -Tracking the first error encountered ------------------------------------- -In some contexts it is useful to track the first error encountered while -allowing execution to continue. Manually writing out ``if`` statements to check -and then assign quickly becomes verbose, and doesn't explicitly highlight the -intended behavior of "latching" to the first error. - -.. code-block:: cpp - - Status overall_status; - for (Sector& sector : sectors) { - Status erase_status = sector.Erase(); - if (!overall_status.ok()) { - overall_status = erase_status; - } - - if (erase_status.ok()) { - Status header_write_status = sector.WriteHeader(); - if (!overall_status.ok()) { - overall_status = header_write_status; - } - } - } - return overall_status; - -:cpp:class:`pw::Status` has a :cpp:func:`pw::Status::Update()` helper function -that does exactly this to reduce visual clutter and succinctly highlight the -intended behavior. - -.. code-block:: cpp - - Status overall_status; - for (Sector& sector : sectors) { - Status erase_status = sector.Erase(); - overall_status.Update(erase_status); - - if (erase_status.ok()) { - overall_status.Update(sector.WriteHeader()); - } - } - return overall_status; - -Unused result warnings ----------------------- -If the ``PW_STATUS_CFG_CHECK_IF_USED`` option is enabled, ``pw::Status`` objects -returned from function calls must be used or it is a compilation error. To -silence these warnings call ``IgnoreError()`` on the returned status object. - -``PW_STATUS_CFG_CHECK_IF_USED`` defaults to off. Pigweed compiles with this -option enabled, but projects that use Pigweed will need to be updated to compile -with this option. After all projects have migrated, unused result warnings will -be enabled unconditionally. - -C compatibility --------------- -``pw_status`` provides the C-compatible :c:enum:`pw_Status` enum for the status -codes. For ease of use, :cpp:class:`pw::Status` implicitly converts to and from -:c:enum:`pw_Status`. However, the :c:enum:`pw_Status` enum should never be used -in C++; instead use the :cpp:class:`pw::Status` class. - -Rust API -======== -``pw_status``'s Rust API is documented in our -`rustdoc API docs `_. - --------------- -StatusWithSize --------------- -``pw::StatusWithSize`` (``pw_status/status_with_size.h``) is a convenient, -efficient class for reporting a status along with an unsigned integer value. -It is similar to the ``pw::Result`` class, but it stores both a size and a -status, regardless of the status value, and only supports a limited range (27 -bits). - -``pw::StatusWithSize`` values may be created with functions similar to -``pw::Status``. For example, - -.. code-block:: cpp - - // An OK StatusWithSize with a size of 123. - StatusWithSize(123) - - // A NOT_FOUND StatusWithSize with a size of 0. - StatusWithSize::NotFound() - - // A RESOURCE_EXHAUSTED StatusWithSize with a size of 10. - StatusWithSize::ResourceExhausted(10) - ------- -PW_TRY ------- -``PW_TRY`` (``pw_status/try.h``) is a convenient set of macros for working -with Status and ``StatusWithSize`` objects in functions that return Status or -``StatusWithSize``. The ``PW_TRY`` and ``PW_TRY_WITH_SIZE`` macros call a -function and do an early return if the function's return status is not ok. - -Example: - -.. code-block:: cpp - - Status PwTryExample() { - PW_TRY(FunctionThatReturnsStatus()); - PW_TRY(FunctionThatReturnsStatusWithSize()); - - // Do something, only executed if both functions above return OK. - } - - StatusWithSize PwTryWithSizeExample() { - PW_TRY_WITH_SIZE(FunctionThatReturnsStatus()); - PW_TRY_WITH_SIZE(FunctionThatReturnsStatusWithSize()); - - // Do something, only executed if both functions above return OK. - } - -``PW_TRY_ASSIGN`` is for working with ``StatusWithSize`` objects in in functions -that return Status. It is similar to ``PW_TRY`` with the addition of assigning -the size from the ``StatusWithSize`` on ok. - -.. code-block:: cpp - - Status PwTryAssignExample() { - size_t size_value - PW_TRY_ASSIGN(size_value, FunctionThatReturnsStatusWithSize()); - - // Do something that uses size_value. size_value is only assigned and this - // following code executed if the PW_TRY_ASSIGN function above returns OK. - } - ------- -Zephyr ------- -To enable ``pw_status`` for Zephyr add ``CONFIG_PIGWEED_STATUS=y`` to the -project's configuration. +Quick reference +--------------- +See :ref:`module-pw_status-codes` for the precise semantics of each error, as +well as how to spell the status in each of our supported languages. Click on +the status names below to jump directly to that error's reference. + +.. list-table:: + :widths: 35 5 60 + :header-rows: 1 + + * - Status + - Code + - Description + * - :c:enumerator:`OK` + - 0 + - Operation succeeded + * - :c:enumerator:`CANCELLED` + - 1 + - Operation was cancelled, typically by the caller + * - :c:enumerator:`UNKNOWN` + - 2 + - Unknown error occurred. Avoid this code when possible. + * - :c:enumerator:`INVALID_ARGUMENT` + - 3 + - Argument was malformed; e.g. invalid characters when parsing integer + * - :c:enumerator:`DEADLINE_EXCEEDED` + - 4 + - Deadline passed before operation completed + * - :c:enumerator:`NOT_FOUND` + - 5 + - The entity that the caller requested (e.g. file or directory) is not + found + * - :c:enumerator:`ALREADY_EXISTS` + - 6 + - The entity that the caller requested to create is already present + * - :c:enumerator:`PERMISSION_DENIED` + - 7 + - Caller lacks permission to execute action + * - :c:enumerator:`RESOURCE_EXHAUSTED` + - 8 + - Insufficient resources to complete operation; e.g. supplied buffer is too + small + * - :c:enumerator:`FAILED_PRECONDITION` + - 9 + - System isn't in the required state; e.g. deleting a non-empty directory + * - :c:enumerator:`ABORTED` + - 10 + - Operation aborted due to e.g. concurrency issue or failed transaction + * - :c:enumerator:`OUT_OF_RANGE` + - 11 + - Operation attempted out of range; e.g. seeking past end of file + * - :c:enumerator:`UNIMPLEMENTED` + - 12 + - Operation isn't implemented or supported + * - :c:enumerator:`INTERNAL` + - 13 + - Internal error occurred; e.g. system invariants were violated + * - :c:enumerator:`UNAVAILABLE` + - 14 + - Requested operation can't finish now, but may at a later time + * - :c:enumerator:`DATA_LOSS` + - 15 + - Unrecoverable data loss occurred while completing the requested operation + * - :c:enumerator:`UNAUTHENTICATED` + - 16 + - Caller does not have valid authentication credentials for the operation + +.. toctree:: + :hidden: + :maxdepth: 1 + + guide + reference diff --git a/pw_status/guide.rst b/pw_status/guide.rst new file mode 100644 index 0000000000..13e49c7a63 --- /dev/null +++ b/pw_status/guide.rst @@ -0,0 +1,210 @@ +.. _module-pw_status-guide: + +==================== +Get started & guides +==================== +.. pigweed-module-subpage:: + :name: pw_status + :tagline: pw_status: Exception-free error propagation for embedded + +.. _module-pw_status-get-started: + +----------- +Get started +----------- +To deploy ``pw_status``, depend on the library: + +.. tab-set:: + + .. tab-item:: Bazel + + Add ``@pigweed//pw_status`` to the ``deps`` list in your Bazel target: + + .. code-block:: + + cc_library("...") { + # ... + deps = [ + # ... + "@pigweed//pw_status", + # ... + ] + } + + This assumes ``@pigweed`` is the name you pulled Pigweed into your Bazel + ``WORKSPACE`` as. + + .. tab-item:: GN + + Add ``$dir_pw_status`` to the ``deps`` list in your ``pw_executable()`` + build target: + + .. code-block:: + + pw_executable("...") { + # ... + deps = [ + # ... + "$dir_pw_status", + # ... + ] + } + + .. tab-item:: CMake + + Add ``pw_status`` to your ``pw_add_library`` or similar CMake target: + + .. code-block:: + + pw_add_library(my_library STATIC + HEADERS + ... + PRIVATE_DEPS + # ... + pw_status + # ... + ) + + .. tab-item:: Zephyr + + There are two ways to use ``pw_status`` from a Zephyr project: + + * Depend on ``pw_status`` in your CMake target (see CMake tab). This is + the Pigweed team's suggested approach since it enables precise CMake + dependency analysis. + + * Add ``CONFIG_PIGWEED_STATUS=y`` to the Zephyr project's configuration, + which causes ``pw_status`` to become a global dependency and have the + includes exposed to all targets. The Pigweed team does not recommend + this approach, though it is the typical Zephyr solution. + +Then use the status object or try macros: + +.. code-block:: cpp + + #include "pw_status/try.h" + #include "pw_status/status.h" + + pw::Status MyOperation() { + PW_TRY(SubOp1()); + PW_TRY(SubOp2()); + // ... + return pw::OkStatus(); + } + +------ +Guides +------ + +Tracking the first error encountered +------------------------------------ +In some contexts it is useful to track the first error encountered while +allowing execution to continue. Manually writing out ``if`` statements to check +and then assign quickly becomes verbose, and doesn't explicitly highlight the +intended behavior of "latching" to the first error. + +.. admonition:: **No**: Track status manually across calls + :class: error + + .. code-block:: cpp + + Status overall_status; + for (Sector& sector : sectors) { + Status erase_status = sector.Erase(); + if (!overall_status.ok()) { + overall_status = erase_status; + } + + if (erase_status.ok()) { + Status header_write_status = sector.WriteHeader(); + if (!overall_status.ok()) { + overall_status = header_write_status; + } + } + } + return overall_status; + +:cpp:class:`pw::Status` has a :cpp:func:`pw::Status::Update()` helper function +that does exactly this to reduce visual clutter and succinctly highlight the +intended behavior. + +.. admonition:: **Yes**: Track status with :cpp:func:`pw::Status::Update()` + :class: checkmark + + .. code-block:: cpp + + Status overall_status; + for (Sector& sector : sectors) { + Status erase_status = sector.Erase(); + overall_status.Update(erase_status); + + if (erase_status.ok()) { + overall_status.Update(sector.WriteHeader()); + } + } + return overall_status; + +---------------------------------- +Jointly reporting status with size +---------------------------------- +``pw::StatusWithSize`` (``pw_status/status_with_size.h``) is a convenient, +efficient class for reporting a status along with an unsigned integer value. +It is similar to the ``pw::Result`` class, but it stores both a size and a +status, regardless of the status value, and only supports a limited range (27 +bits). + +``pw::StatusWithSize`` values may be created with functions similar to +``pw::Status``. For example, + +.. code-block:: cpp + + // An OK StatusWithSize with a size of 123. + StatusWithSize(123) + + // A NOT_FOUND StatusWithSize with a size of 0. + StatusWithSize::NotFound() + + // A RESOURCE_EXHAUSTED StatusWithSize with a size of 10. + StatusWithSize::ResourceExhausted(10) + +----------------------------------- +Reducing error handling boilerplate +----------------------------------- +Manual error handling through return codes is easy to understand and +straightforward to write, but leads to verbose code. To reduce boilerplate, +Pigweed has the ``PW_TRY`` (``pw_status/try.h``) macro, easing development of +functions checking or returning ``pw::Status`` and ``pw::StatusWithSize`` +objects. The ``PW_TRY`` and ``PW_TRY_WITH_SIZE`` macros call a function and do +an early return if the function's return status is not :c:enumerator:`OK`. + +Example: + +.. code-block:: cpp + + Status PwTryExample() { + PW_TRY(FunctionThatReturnsStatus()); + PW_TRY(FunctionThatReturnsStatusWithSize()); + + // Do something, only executed if both functions above return OK. + } + + StatusWithSize PwTryWithSizeExample() { + PW_TRY_WITH_SIZE(FunctionThatReturnsStatus()); + PW_TRY_WITH_SIZE(FunctionThatReturnsStatusWithSize()); + + // Do something, only executed if both functions above return OK. + } + +``PW_TRY_ASSIGN`` is for working with ``pw::StatusWithSize`` objects in in +functions that return Status. It is similar to ``PW_TRY`` with the addition of +assigning the size from the ``pw::StatusWithSize`` on ok. + +.. code-block:: cpp + + Status PwTryAssignExample() { + size_t size_value + PW_TRY_ASSIGN(size_value, FunctionThatReturnsStatusWithSize()); + + // Do something that uses size_value. size_value is only assigned and this + // following code executed if the PW_TRY_ASSIGN function above returns OK. + } diff --git a/pw_status/reference.rst b/pw_status/reference.rst new file mode 100644 index 0000000000..45a3458159 --- /dev/null +++ b/pw_status/reference.rst @@ -0,0 +1,400 @@ +.. _module-pw_status-reference: + +========= +Reference +========= +.. pigweed-module-subpage:: + :name: pw_status + :tagline: pw_status: Exception-free error propagation for embedded + +.. _module-pw_status-codes: + +------------ +Status codes +------------ +.. c:enumerator:: OK = 0 + + :c:enumerator:`OK` indicates that the operation completede successfully. 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 ``ok()`` member function + rather than inspecting the raw code. + + .. list-table:: + + * - C++ + - ``pw::OkStatus()`` + * - C + - ``PW_STATUS_OK`` + * - Python / Java / TypeScript + - ``Status.OK`` + * - Rust + - ``Ok(val)`` + +.. c:enumerator:: CANCELLED = 1 + + :c:enumerator:`CANCELLED` indicates the operation was cancelled, typically by + the caller. + + .. list-table:: + + * - C++ + - ``pw::Status::Cancelled()`` + * - C + - ``PW_STATUS_CANCELLED`` + * - Python / Java / TypeScript + - ``Status.CANCELLED`` + * - Rust + - ``Error::Cancelled`` + +.. c:enumerator:: UNKNOWN = 2 + + :c:enumerator:`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. + + .. list-table:: + + * - C++ + - ``pw::Status::Unknown()`` + * - C + - ``PW_STATUS_UNKNOWN`` + * - Python / Java / TypeScript + - ``Status.UNKNOWN`` + * - Rust + - ``Error::Unknown`` + +.. c:enumerator:: INVALID_ARGUMENT = 3 + + :c:enumerator:`INVALID_ARGUMENT` indicates the caller specified an invalid + argument, such as a malformed filename. Note that use of such errors should + be narrowly limited to indicate 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 + :c:enumerator:`FAILED_PRECONDITION` instead. + + .. list-table:: + + * - C++ + - ``pw::Status::InvalidArgument()`` + * - C + - ``PW_STATUS_INVALID_ARGUMENT`` + * - Python / Java / TypeScript + - ``Status.INVALID_ARGUMENT`` + * - Rust + - ``Error::InvalidArgument`` + +.. c:enumerator:: DEADLINE_EXCEEDED = 4 + + :c:enumerator:`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. + + .. list-table:: + + * - C++ + - ``pw::Status::DeadlineExceeded()`` + * - C + - ``PW_STATUS_DEADLINE_EXCEEDED`` + * - Python / Java / TypeScript + - ``Status.DEADLINE_EXCEEDED`` + * - Rust + - ``Error::DeadlineExceeded`` + +.. c:enumerator:: NOT_FOUND = 5 + + :c:enumerator:`NOT_FOUND` indicates some requested entity (such as a file or + directory) was not found. + + :c:enumerator:`NOT_FOUND` is useful if a request should be denied for an + entire class of users, such as during a gradual feature rollout or + undocumented allowlist. If a request should be denied for specific sets of + users, such as through user-based access control, use + :c:enumerator:`PERMISSION_DENIED` instead. + + .. list-table:: + + * - C++ + - ``pw::Status::NotFound()`` + * - C + - ``PW_STATUS_NOT_FOUND`` + * - Python / Java / TypeScript + - ``Status.NOT_FOUND`` + * - Rust + - ``Error::NotFound`` + +.. c:enumerator:: ALREADY_EXISTS = 6 + + :c:enumerator:`ALREADY_EXISTS` indicates that the entity a caller attempted + to create (such as a file or directory) is already present. + + .. list-table:: + + * - C++ + - ``pw::Status::AlreadyExists()`` + * - C + - ``PW_STATUS_ALREADY_EXISTS`` + * - Python / Java / TypeScript + - ``Status.ALREADY_EXISTS`` + * - Rust + - ``Error::AlreadyExists`` + +.. c:enumerator:: PERMISSION_DENIED = 7 + + :c:enumerator:`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 unauthenticated user. This error code does + not imply the request is valid or the requested entity exists or satisfies + any other pre-conditions. + + :c:enumerator:`PERMISSION_DENIED` must not be used for rejections caused by + exhausting some resource. Instead, use :c:enumerator:`RESOURCE_EXHAUSTED` for + those errors. :c:enumerator:`PERMISSION_DENIED` must not be used if the + caller cannot be identified. Instead, use :c:enumerator:`UNAUTHENTICATED` + for those errors. + + .. list-table:: + + * - C++ + - ``pw::Status::PermissionDenied()`` + * - C + - ``PW_STATUS_PERMISSION_DENIED`` + * - Python / Java / TypeScript + - ``Status.PERMISSION_DENIED`` + * - Rust + - ``Error::PermissionDenied`` + +.. c:enumerator:: RESOURCE_EXHAUSTED = 8 + + :c:enumerator:`RESOURCE_EXHAUSTED` indicates some resource has been + exhausted, perhaps a per-user quota, or perhaps the entire file system is out + of space. + + .. list-table:: + + * - C++ + - ``pw::Status::ResourceExhausted()`` + * - C + - ``PW_STATUS_RESOURCE_EXHAUSTED`` + * - Python / Java / TypeScript + - ``Status.RESOURCE_EXHAUSTED`` + * - Rust + - ``Error::ResourceExhausted`` + +.. c:enumerator:: FAILED_PRECONDITION = 9 + + :c:enumerator:`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. + + .. _module-pw_status-guidelines: + + Some guidelines that may help a service implementer in deciding between + :c:enumerator:`FAILED_PRECONDITION`, :c:enumerator:`ABORTED`, and + :c:enumerator:`UNAVAILABLE`: + + a. Use :c:enumerator:`UNAVAILABLE` if the client can retry just the failing + call. + b. Use :c:enumerator:`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 :c:enumerator:`FAILED_PRECONDITION` if the client should not retry + until the system state has been explicitly fixed. For example, if a + ``rmdir`` fails because the directory is non-empty, + :c:enumerator:`FAILED_PRECONDITION` should be returned since the client + should not retry unless the files are deleted from the directory. + + .. list-table:: + + * - C++ + - ``pw::Status::FailedPrecondition()`` + * - C + - ``PW_STATUS_FAILED_PRECONDITION`` + * - Python / Java / TypeScript + - ``Status.FAILED_PRECONDITION`` + * - Rust + - ``Error::FailedPrecondition`` + +.. c:enumerator:: ABORTED = 10 + + :c:enumerator:`ABORTED` indicates the operation was aborted, typically due to + a concurrency issue such as a sequencer check failure or a failed + transaction. + + See the :ref:`guidelines ` above for deciding + between :c:enumerator:`FAILED_PRECONDITION`, :c:enumerator:`ABORTED`, and + :c:enumerator:`UNAVAILABLE`. + + .. list-table:: + + * - C++ + - ``pw::Status::Aborted()`` + * - C + - ``PW_STATUS_ABORTED`` + * - Python / Java / TypeScript + - ``Status.ABORTED`` + * - Rust + - ``Error::Aborted`` + +.. c:enumerator:: OUT_OF_RANGE = 11 + + :c:enumerator:`OUT_OF_RANGE` indicates the operation was attempted past the + valid range, such as seeking or reading past an end-of-file. + + Unlike :c:enumerator:`INVALID_ARGUMENT`, this error indicates a problem that + may be fixed if the system state changes. For example, a 32-bit file system + will generate :c:enumerator:`INVALID_ARGUMENT` if asked to read at an offset + that is not in the range [0,2^32-1], but it will generate + :c:enumerator:`OUT_OF_RANGE` if asked to read from an offset past the current + file size. + + There is a fair bit of overlap between :c:enumerator:`FAILED_PRECONDITION` + and :c:enumerator:`OUT_OF_RANGE`. We recommend using + :c:enumerator:`OUT_OF_RANGE` (the more specific error) when it applies so + that callers who are iterating through a space can easily look for an + :c:enumerator:`OUT_OF_RANGE` error to detect when they are done. + + .. list-table:: + + * - C++ + - ``pw::Status::OutOfRange()`` + * - C + - ``PW_STATUS_OUT_OF_RANGE`` + * - Python / Java / TypeScript + - ``Status.OUT_OF_RANGE`` + * - Rust + - ``Error::OutOfRange`` + +.. c:enumerator:: UNIMPLEMENTED = 12 + + :c:enumerator:`UNIMPLEMENTED` indicates the operation is not implemented or + supported in this service. In this case, the operation should not be + re-attempted. + + .. list-table:: + + * - C++ + - ``pw::Status::Unimplemented()`` + * - C + - ``PW_STATUS_UNIMPLEMENTED`` + * - Python / Java / TypeScript + - ``Status.UNIMPLEMENTED`` + * - Rust + - ``Error::Unimplemented`` + +.. c:enumerator:: INTERNAL = 13 + + :c:enumerator:`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. + + .. list-table:: + + * - C++ + - ``pw::Status::Internal()`` + * - C + - ``PW_STATUS_INTERNAL`` + * - Python / Java / TypeScript + - ``Status.INTERNAL`` + * - Rust + - ``Error::Internal`` + +.. c:enumerator:: UNAVAILABLE = 14 + + :c:enumerator:`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 :ref:`guidelines ` above for deciding + between :c:enumerator:`FAILED_PRECONDITION`, :c:enumerator:`ABORTED`, and + :c:enumerator:`UNAVAILABLE`. + + .. list-table:: + + * - C++ + - ``pw::Status::Unavailable()`` + * - C + - ``PW_STATUS_UNAVAILABLE`` + * - Python / Java / TypeScript + - ``Status.UNAVAILABLE`` + * - Rust + - ``Error::Unavailable`` + +.. c:enumerator:: DATA_LOSS = 15 + + :c:enumerator:`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. + + .. list-table:: + + * - C++ + - ``pw::Status::DataLoss()`` + * - C + - ``PW_STATUS_DATA_LOSS`` + * - Python / Java / TypeScript + - ``Status.DATA_LOSS`` + * - Rust + - ``Error::DataLoss`` + +.. c:enumerator:: UNAUTHENTICATED = 16 + + :c:enumerator:`UNAUTHENTICATED` indicates that the request does not have + valid authentication credentials for the operation. Correct the + authentication and try again. + + .. list-table:: + + * - C++ + - ``pw::Status::Unauthenticated()`` + * - C + - ``PW_STATUS_UNAUTHENTICATED`` + * - Python / Java / TypeScript + - ``Status.UNAUTHENTICATED`` + * - Rust + - ``Error::Unauthenticated`` + +------- +C++ API +------- +.. doxygenclass:: pw::Status + :members: + +.. doxygenfunction:: pw::OkStatus + +.. c:enum:: pw_Status + + Enum to use in place of :cpp:class:`pw::Status` in C code. Always use + :cpp:class:`pw::Status` in C++ code. + + The values of the :c:enum:`pw_Status` enum are all-caps and prefixed with + ``PW_STATUS_``. For example, ``PW_STATUS_DATA_LOSS`` corresponds with + :c:enumerator:`DATA_LOSS`. + +Unused result warnings +---------------------- +If the ``PW_STATUS_CFG_CHECK_IF_USED`` option is enabled, ``pw::Status`` objects +returned from function calls must be used or it is a compilation error. To +silence these warnings call ``IgnoreError()`` on the returned status object. + +``PW_STATUS_CFG_CHECK_IF_USED`` defaults to ``false``. Pigweed compiles with +this option enabled, but projects that use Pigweed will need to be updated to +compile with this option. After all projects have migrated, unused result +warnings will be enabled unconditionally. + +----- +C API +----- +``pw_status`` provides the C-compatible :c:enum:`pw_Status` enum for the status +codes. For ease of use, :cpp:class:`pw::Status` implicitly converts to and from +:c:enum:`pw_Status`. However, the :c:enum:`pw_Status` enum should never be used +in C++; instead use the :cpp:class:`pw::Status` class. + +-------- +Rust API +-------- +``pw_status``'s Rust API is documented in our +`rustdoc API docs `_.