FTL: Add FTL_EXPECT

Like FTL_TRY, FTL_EXPECT unwraps T for Expected<T, E> or does an early
out on error, but it bails out with E instead of Expected<T, E>.

Fix a line in the Expected.Try test to call the intended helper.

Bug: 185536303
Test: ftl_test
Change-Id: I238ae2978ff606c5c035e9791496c2b20729ca7f
diff --git a/include/ftl/expected.h b/include/ftl/expected.h
index 57448dc..7e765c5 100644
--- a/include/ftl/expected.h
+++ b/include/ftl/expected.h
@@ -69,6 +69,36 @@
     exp_.value();                                                         \
   })
 
+// Given an expression `expr` that evaluates to an ftl::Expected<T, E> result (R for short),
+// FTL_EXPECT unwraps T out of R, or bails out of the enclosing function F if R has an error E.
+// While FTL_TRY bails out with R, FTL_EXPECT bails out with E, which is useful when F does not
+// need to propagate R because T is not relevant to the caller.
+//
+// Example usage:
+//
+//   using StringExp = ftl::Expected<std::string, std::errc>;
+//
+//   std::errc repeat(StringExp exp, std::string& out) {
+//     const std::string str = FTL_EXPECT(exp);
+//     out = str + str;
+//     return std::errc::operation_in_progress;
+//   }
+//
+//   std::string str;
+//   assert(std::errc::operation_in_progress == repeat(StringExp("ha"s), str));
+//   assert("haha"s == str);
+//   assert(std::errc::bad_message == repeat(ftl::Unexpected(std::errc::bad_message), str));
+//   assert("haha"s == str);
+//
+#define FTL_EXPECT(expr)              \
+  ({                                  \
+    auto exp_ = (expr);               \
+    if (!exp_.has_value()) {          \
+      return std::move(exp_.error()); \
+    }                                 \
+    exp_.value();                     \
+  })
+
 namespace android::ftl {
 
 // Superset of base::expected<T, E> with monadic operations.
diff --git a/libs/ftl/expected_test.cpp b/libs/ftl/expected_test.cpp
index 9b7f017..d5b1d7e 100644
--- a/libs/ftl/expected_test.cpp
+++ b/libs/ftl/expected_test.cpp
@@ -79,16 +79,28 @@
 
 namespace {
 
-IntExp increment(IntExp exp) {
+IntExp increment_try(IntExp exp) {
   const int i = FTL_TRY(exp);
   return IntExp(i + 1);
 }
 
-StringExp repeat(StringExp exp) {
+std::errc increment_expect(IntExp exp, int& out) {
+  const int i = FTL_EXPECT(exp);
+  out = i + 1;
+  return std::errc::operation_in_progress;
+}
+
+StringExp repeat_try(StringExp exp) {
   const std::string str = FTL_TRY(exp);
   return StringExp(str + str);
 }
 
+std::errc repeat_expect(StringExp exp, std::string& out) {
+  const std::string str = FTL_EXPECT(exp);
+  out = str + str;
+  return std::errc::operation_in_progress;
+}
+
 void uppercase(char& c, ftl::Optional<char> opt) {
   c = std::toupper(FTL_TRY(std::move(opt).ok_or(ftl::Unit())));
 }
@@ -97,13 +109,13 @@
 
 // Keep in sync with example usage in header file.
 TEST(Expected, Try) {
-  EXPECT_EQ(IntExp(100), increment(IntExp(99)));
-  EXPECT_TRUE(repeat(ftl::Unexpected(std::errc::value_too_large)).has_error([](std::errc e) {
+  EXPECT_EQ(IntExp(100), increment_try(IntExp(99)));
+  EXPECT_TRUE(increment_try(ftl::Unexpected(std::errc::value_too_large)).has_error([](std::errc e) {
     return e == std::errc::value_too_large;
   }));
 
-  EXPECT_EQ(StringExp("haha"s), repeat(StringExp("ha"s)));
-  EXPECT_TRUE(repeat(ftl::Unexpected(std::errc::bad_message)).has_error([](std::errc e) {
+  EXPECT_EQ(StringExp("haha"s), repeat_try(StringExp("ha"s)));
+  EXPECT_TRUE(repeat_try(ftl::Unexpected(std::errc::bad_message)).has_error([](std::errc e) {
     return e == std::errc::bad_message;
   }));
 
@@ -115,4 +127,19 @@
   EXPECT_EQ(c, 'A');
 }
 
+TEST(Expected, Expect) {
+  int i = 0;
+  EXPECT_EQ(std::errc::operation_in_progress, increment_expect(IntExp(99), i));
+  EXPECT_EQ(100, i);
+  EXPECT_EQ(std::errc::value_too_large,
+            increment_expect(ftl::Unexpected(std::errc::value_too_large), i));
+  EXPECT_EQ(100, i);
+
+  std::string str;
+  EXPECT_EQ(std::errc::operation_in_progress, repeat_expect(StringExp("ha"s), str));
+  EXPECT_EQ("haha"s, str);
+  EXPECT_EQ(std::errc::bad_message, repeat_expect(ftl::Unexpected(std::errc::bad_message), str));
+  EXPECT_EQ("haha"s, str);
+}
+
 }  // namespace android::test