pw_result: add PW_TRY_ASSIGN support

Adds PW_TRY_ASSIGN to pw::Result<T>.

Change-Id: I7cc73c14f7f36bc048098abd17f543b8ddf252af
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/41900
Reviewed-by: Wyatt Hepler <hepler@google.com>
Pigweed-Auto-Submit: Ewout van Bekkum <ewout@google.com>
Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com>
diff --git a/pw_result/BUILD b/pw_result/BUILD
index e2d6b59..7566d23 100644
--- a/pw_result/BUILD
+++ b/pw_result/BUILD
@@ -39,6 +39,7 @@
     srcs = ["result_test.cc"],
     deps = [
         ":pw_result",
+        "//pw_status",
         "//pw_unit_test",
     ],
 )
diff --git a/pw_result/BUILD.gn b/pw_result/BUILD.gn
index 31cd575..dc4f2e7 100644
--- a/pw_result/BUILD.gn
+++ b/pw_result/BUILD.gn
@@ -37,7 +37,10 @@
 }
 
 pw_test("result_test") {
-  deps = [ ":pw_result" ]
+  deps = [
+    ":pw_result",
+    dir_pw_status,
+  ]
   sources = [ "result_test.cc" ]
 }
 
diff --git a/pw_result/docs.rst b/pw_result/docs.rst
index bfc37cd..b628bbd 100644
--- a/pw_result/docs.rst
+++ b/pw_result/docs.rst
@@ -7,6 +7,44 @@
 data when the status is OK. This is meant for returning lightweight result
 types or references to larger results.
 
+``pw::Result`` is compatible with ``PW_TRY`` and ``PW_TRY_ASSIGN``, for example:
+
+.. code-block:: cpp
+
+  #include "pw_status/try.h"
+  #include "pw_result/result.h"
+
+  pw::Result<int> GetAnswer();  // Example function.
+
+  pw::Status UseAnswer() {
+    const pw::Result<int> answer = GetAnswer();
+    if (!answer.ok()) {
+      return answer.status();
+    }
+    if (answer.value() == 42) {
+      WhatWasTheUltimateQuestion();
+    }
+    return pw::OkStatus();
+  }
+
+  pw::Status UseAnswerWithTry() {
+    const pw::Result<int> answer = GetAnswer();
+    PW_TRY(answer.status());
+    if (answer.value() == 42) {
+      WhatWasTheUltimateQuestion();
+    }
+    return pw::OkStatus();
+  }
+
+  pw::Status UseAnswerWithTryAssign() {
+    PW_TRY_ASSIGN(const int answer, GetAnswer());
+    if (answer == 42) {
+      WhatWasTheUltimateQuestion();
+    }
+    return pw::OkStatus();
+  }
+
+
 .. warning::
 
   Be careful not to use larger types by value as this can quickly consume
diff --git a/pw_result/public/pw_result/result.h b/pw_result/public/pw_result/result.h
index e529344..6c93697 100644
--- a/pw_result/public/pw_result/result.h
+++ b/pw_result/public/pw_result/result.h
@@ -14,6 +14,7 @@
 #pragma once
 
 #include <algorithm>
+#include <utility>
 
 #include "pw_assert/light.h"
 #include "pw_status/status.h"
@@ -23,6 +24,9 @@
 // A Result represents the result of an operation which can fail. It is a
 // convenient wrapper around returning a Status alongside some data when the
 // status is OK.
+//
+// TODO(pwbug/363): Refactor pw::Result to properly support non-default move
+// and/or copy assignment operators and/or constructors.
 template <typename T>
 class Result {
  public:
@@ -96,4 +100,17 @@
   Status status_;
 };
 
+namespace internal {
+
+template <typename T>
+constexpr Status ConvertToStatus(const Result<T>& result) {
+  return result.status();
+}
+
+template <typename T>
+constexpr T ConvertToValue(Result<T>& result) {
+  return std::move(result.value());
+}
+
+}  // namespace internal
 }  // namespace pw
diff --git a/pw_result/result_test.cc b/pw_result/result_test.cc
index e9095f5..6642cf3 100644
--- a/pw_result/result_test.cc
+++ b/pw_result/result_test.cc
@@ -15,6 +15,7 @@
 #include "pw_result/result.h"
 
 #include "gtest/gtest.h"
+#include "pw_status/try.h"
 
 namespace pw {
 namespace {
@@ -72,5 +73,28 @@
   EXPECT_EQ(res.status(), Status::InvalidArgument());
 }
 
+Result<bool> ReturnResult(Result<bool> result) { return result; }
+
+Status TryResultAssign(Result<bool> result) {
+  PW_TRY_ASSIGN(const bool value, ReturnResult(result));
+
+  // Any status other than OK should have already returned.
+  EXPECT_EQ(result.status(), OkStatus());
+  EXPECT_EQ(value, result.value());
+  return result.status();
+}
+
+// TODO(pwbug/363): Once pw::Result has been refactored to properly support
+// non-default move and/or copy assignment operators and/or constructors, we
+// should add explicit tests to confirm this is properly handled by
+// PW_TRY_ASSIGN.
+TEST(Result, TryAssign) {
+  EXPECT_EQ(TryResultAssign(Status::Cancelled()), Status::Cancelled());
+  EXPECT_EQ(TryResultAssign(Status::DataLoss()), Status::DataLoss());
+  EXPECT_EQ(TryResultAssign(Status::Unimplemented()), Status::Unimplemented());
+  EXPECT_EQ(TryResultAssign(false), OkStatus());
+  EXPECT_EQ(TryResultAssign(true), OkStatus());
+}
+
 }  // namespace
 }  // namespace pw
diff --git a/pw_status/public/pw_status/try.h b/pw_status/public/pw_status/try.h
index 76e7a19..38ab26e 100644
--- a/pw_status/public/pw_status/try.h
+++ b/pw_status/public/pw_status/try.h
@@ -13,6 +13,8 @@
 // the License.
 #pragma once
 
+#include <utility>
+
 #include "pw_status/status.h"
 #include "pw_status/status_with_size.h"
 
@@ -35,7 +37,7 @@
   if (!result.ok()) {                               \
     return ::pw::internal::ConvertToStatus(result); \
   }                                                 \
-  lhs = std::move(result.size())
+  lhs = ::pw::internal::ConvertToValue(result);
 
 // Macro for cleanly working with Status or StatusWithSize objects in functions
 // that return StatusWithSize.
@@ -59,6 +61,10 @@
   return status_with_size.status();
 }
 
+constexpr size_t ConvertToValue(StatusWithSize status_with_size) {
+  return status_with_size.size();
+}
+
 constexpr StatusWithSize ConvertToStatusWithSize(Status status) {
   return StatusWithSize(status, 0);
 }