pw_status: Add Update()

Adds an Abseil-style Update() helper to pw::Status that makes it easier
to track the first error encountered by a Status object.

Change-Id: I0aaca4c49e68daa578c8a39ffce726ae0c6dc5b3
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/42620
Pigweed-Auto-Submit: Armando Montanez <amontanez@google.com>
Reviewed-by: Wyatt Hepler <hepler@google.com>
Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com>
diff --git a/pw_status/docs.rst b/pw_status/docs.rst
index 0f01ea8..c2595c5 100644
--- a/pw_status/docs.rst
+++ b/pw_status/docs.rst
@@ -179,6 +179,47 @@
   The Python tool ``pw_status/update_style.py`` may be used to migrate code in a
   Git repo to the new status style.
 
+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;
+
+``pw::Status`` has an ``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;
+
 C compatibility
 ---------------
 ``pw_status`` provides the C-compatible ``pw_Status`` enum for the status codes.
diff --git a/pw_status/public/pw_status/status.h b/pw_status/public/pw_status/status.h
index ae3b852..eb02440 100644
--- a/pw_status/public/pw_status/status.h
+++ b/pw_status/public/pw_status/status.h
@@ -303,6 +303,15 @@
     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();
+    }
+  }
+
   // Returns a null-terminated string representation of the Status.
   [[nodiscard]] const char* str() const { return pw_StatusString(code_); }
 
diff --git a/pw_status/status_test.cc b/pw_status/status_test.cc
index 0bf72f5..33102b7 100644
--- a/pw_status/status_test.cc
+++ b/pw_status/status_test.cc
@@ -156,6 +156,16 @@
   EXPECT_STREQ("INVALID STATUS", Status(kInvalidCode).str());
 }
 
+TEST(Status, Update) {
+  Status status;
+  status.Update(Status::Cancelled());
+  EXPECT_EQ(status, Status::Cancelled());
+  status.Update(OkStatus());
+  EXPECT_EQ(status, Status::Cancelled());
+  status.Update(Status::NotFound());
+  EXPECT_EQ(status, Status::Cancelled());
+}
+
 // Functions for executing the C pw_Status tests.
 extern "C" {