pw_protobuf: Return a Result from Encode()

This updates Encoder::Encode() to return a Result to make it less
awkward to use and to ensure that its status is checked by the caller.

Change-Id: Ie376ee07555199010e4363d013959bb24b0db79a
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/19620
Reviewed-by: Ewout van Bekkum <ewout@google.com>
Reviewed-by: Wyatt Hepler <hepler@google.com>
Commit-Queue: Alexei Frolov <frolv@google.com>
diff --git a/pw_protobuf/BUILD.gn b/pw_protobuf/BUILD.gn
index b4638a7..c4ee3ed 100644
--- a/pw_protobuf/BUILD.gn
+++ b/pw_protobuf/BUILD.gn
@@ -28,8 +28,10 @@
 pw_source_set("pw_protobuf") {
   public_configs = [ ":default_config" ]
   public_deps = [
-    "$dir_pw_status",
-    "$dir_pw_varint",
+    dir_pw_bytes,
+    dir_pw_result,
+    dir_pw_status,
+    dir_pw_varint,
   ]
   public = [
     "public/pw_protobuf/codegen.h",
diff --git a/pw_protobuf/codegen_test.cc b/pw_protobuf/codegen_test.cc
index 0b26657..72e11a7 100644
--- a/pw_protobuf/codegen_test.cc
+++ b/pw_protobuf/codegen_test.cc
@@ -166,10 +166,11 @@
   };
   // clang-format on
 
-  std::span<const std::byte> proto;
-  EXPECT_EQ(encoder.Encode(&proto), Status::Ok());
-  EXPECT_EQ(proto.size(), sizeof(expected_proto));
-  EXPECT_EQ(std::memcmp(proto.data(), expected_proto, sizeof(expected_proto)),
+  Result result = encoder.Encode();
+  ASSERT_EQ(result.status(), Status::Ok());
+  EXPECT_EQ(result.value().size(), sizeof(expected_proto));
+  EXPECT_EQ(std::memcmp(
+                result.value().data(), expected_proto, sizeof(expected_proto)),
             0);
 }
 
@@ -185,10 +186,11 @@
   constexpr uint8_t expected_proto[] = {
       0x08, 0x00, 0x08, 0x10, 0x08, 0x20, 0x08, 0x30};
 
-  std::span<const std::byte> proto;
-  EXPECT_EQ(encoder.Encode(&proto), Status::Ok());
-  EXPECT_EQ(proto.size(), sizeof(expected_proto));
-  EXPECT_EQ(std::memcmp(proto.data(), expected_proto, sizeof(expected_proto)),
+  Result result = encoder.Encode();
+  ASSERT_EQ(result.status(), Status::Ok());
+  EXPECT_EQ(result.value().size(), sizeof(expected_proto));
+  EXPECT_EQ(std::memcmp(
+                result.value().data(), expected_proto, sizeof(expected_proto)),
             0);
 }
 
@@ -201,10 +203,11 @@
   repeated_test.WriteUint32s(values);
 
   constexpr uint8_t expected_proto[] = {0x0a, 0x04, 0x00, 0x10, 0x20, 0x30};
-  std::span<const std::byte> proto;
-  EXPECT_EQ(encoder.Encode(&proto), Status::Ok());
-  EXPECT_EQ(proto.size(), sizeof(expected_proto));
-  EXPECT_EQ(std::memcmp(proto.data(), expected_proto, sizeof(expected_proto)),
+  Result result = encoder.Encode();
+  ASSERT_EQ(result.status(), Status::Ok());
+  EXPECT_EQ(result.value().size(), sizeof(expected_proto));
+  EXPECT_EQ(std::memcmp(
+                result.value().data(), expected_proto, sizeof(expected_proto)),
             0);
 }
 
@@ -221,10 +224,11 @@
   constexpr uint8_t expected_proto[] = {
       0x1a, 0x03, 't', 'h', 'e', 0x1a, 0x5, 'q',  'u', 'i', 'c', 'k',
       0x1a, 0x5,  'b', 'r', 'o', 'w',  'n', 0x1a, 0x3, 'f', 'o', 'x'};
-  std::span<const std::byte> proto;
-  EXPECT_EQ(encoder.Encode(&proto), Status::Ok());
-  EXPECT_EQ(proto.size(), sizeof(expected_proto));
-  EXPECT_EQ(std::memcmp(proto.data(), expected_proto, sizeof(expected_proto)),
+  Result result = encoder.Encode();
+  ASSERT_EQ(result.status(), Status::Ok());
+  EXPECT_EQ(result.value().size(), sizeof(expected_proto));
+  EXPECT_EQ(std::memcmp(
+                result.value().data(), expected_proto, sizeof(expected_proto)),
             0);
 }
 
@@ -245,10 +249,11 @@
     0x01, 0x10, 0x02, 0x2a, 0x04, 0x08, 0x02, 0x10, 0x04};
   // clang-format on
 
-  std::span<const std::byte> proto;
-  EXPECT_EQ(encoder.Encode(&proto), Status::Ok());
-  EXPECT_EQ(proto.size(), sizeof(expected_proto));
-  EXPECT_EQ(std::memcmp(proto.data(), expected_proto, sizeof(expected_proto)),
+  Result result = encoder.Encode();
+  ASSERT_EQ(result.status(), Status::Ok());
+  EXPECT_EQ(result.value().size(), sizeof(expected_proto));
+  EXPECT_EQ(std::memcmp(
+                result.value().data(), expected_proto, sizeof(expected_proto)),
             0);
 }
 
@@ -269,10 +274,11 @@
   constexpr uint8_t expected_proto[] = {
       0x08, 0x03, 0x1a, 0x06, 0x0a, 0x04, 0xde, 0xad, 0xbe, 0xef};
 
-  std::span<const std::byte> proto;
-  EXPECT_EQ(encoder.Encode(&proto), Status::Ok());
-  EXPECT_EQ(proto.size(), sizeof(expected_proto));
-  EXPECT_EQ(std::memcmp(proto.data(), expected_proto, sizeof(expected_proto)),
+  Result result = encoder.Encode();
+  ASSERT_EQ(result.status(), Status::Ok());
+  EXPECT_EQ(result.value().size(), sizeof(expected_proto));
+  EXPECT_EQ(std::memcmp(
+                result.value().data(), expected_proto, sizeof(expected_proto)),
             0);
 }
 
@@ -293,8 +299,7 @@
     end.WriteNanoseconds(490367432);
   }
 
-  std::span<const std::byte> proto;
-  EXPECT_EQ(encoder.Encode(&proto), Status::Ok());
+  EXPECT_EQ(encoder.Encode().status(), Status::Ok());
 }
 
 TEST(Codegen, NonPigweedPackage) {
@@ -306,8 +311,7 @@
   packed.WriteRep(std::span<const int64_t>(repeated));
   packed.WritePacked("packed");
 
-  std::span<const std::byte> proto;
-  EXPECT_EQ(encoder.Encode(&proto), Status::Ok());
+  EXPECT_EQ(encoder.Encode().status(), Status::Ok());
 }
 
 }  // namespace
diff --git a/pw_protobuf/encoder.cc b/pw_protobuf/encoder.cc
index 6a5eceb..66174fc 100644
--- a/pw_protobuf/encoder.cc
+++ b/pw_protobuf/encoder.cc
@@ -126,16 +126,14 @@
   return Status::Ok();
 }
 
-Status Encoder::Encode(std::span<const std::byte>* out) {
+Result<ConstByteSpan> Encoder::Encode() {
   if (!encode_status_.ok()) {
-    *out = std::span<const std::byte>();
     return encode_status_;
   }
 
   if (blob_count_ == 0) {
     // If there are no nested blobs, the buffer already contains a valid proto.
-    *out = buffer_.first(EncodedSize());
-    return Status::Ok();
+    return Result<ConstByteSpan>(buffer_.first(EncodedSize()));
   }
 
   union {
@@ -179,8 +177,7 @@
 
   // Point the cursor to the end of the encoded proto.
   cursor_ = write_cursor;
-  *out = buffer_.first(EncodedSize());
-  return Status::Ok();
+  return Result<ConstByteSpan>(buffer_.first(EncodedSize()));
 }
 
 }  // namespace pw::protobuf
diff --git a/pw_protobuf/encoder_fuzzer.cc b/pw_protobuf/encoder_fuzzer.cc
index e0991d0..1ede047 100644
--- a/pw_protobuf/encoder_fuzzer.cc
+++ b/pw_protobuf/encoder_fuzzer.cc
@@ -133,7 +133,6 @@
   ASAN_POISON_MEMORY_REGION(poisoned, poisoned_length);
 
   pw::protobuf::NestedEncoder encoder(unpoisoned);
-  std::span<const std::byte> out;
 
   // Storage for generated spans
   std::vector<uint32_t> u32s;
@@ -154,7 +153,7 @@
     switch (provider.ConsumeEnum<FieldType>()) {
       case kEncodeAndClear:
         // Special "field". Encode all the fields so far and reset the encoder.
-        encoder.Encode(&out);
+        encoder.Encode();
         encoder.Clear();
         break;
       case kUint32:
@@ -278,7 +277,7 @@
     }
   }
   // Ensure we call `Encode` at least once.
-  encoder.Encode(&out);
+  encoder.Encode();
 
   // Don't forget to unpoison for the next iteration!
   ASAN_UNPOISON_MEMORY_REGION(poisoned, poisoned_length);
diff --git a/pw_protobuf/encoder_test.cc b/pw_protobuf/encoder_test.cc
index 109943d..605467b 100644
--- a/pw_protobuf/encoder_test.cc
+++ b/pw_protobuf/encoder_test.cc
@@ -93,10 +93,12 @@
   EXPECT_EQ(encoder.WriteString(kTestProtoErrorMessageField, "broken 💩"),
             Status::Ok());
 
-  std::span<const std::byte> encoded;
-  EXPECT_EQ(encoder.Encode(&encoded), Status::Ok());
-  EXPECT_EQ(encoded.size(), sizeof(encoded_proto));
-  EXPECT_EQ(std::memcmp(encoded.data(), encoded_proto, encoded.size()), 0);
+  Result result = encoder.Encode();
+  ASSERT_EQ(result.status(), Status::Ok());
+  EXPECT_EQ(result.value().size(), sizeof(encoded_proto));
+  EXPECT_EQ(
+      std::memcmp(result.value().data(), encoded_proto, sizeof(encoded_proto)),
+      0);
 }
 
 TEST(Encoder, EncodeInsufficientSpace) {
@@ -115,9 +117,7 @@
   EXPECT_EQ(encoder.WriteFloat(kTestProtoRatioField, 1.618034),
             Status::ResourceExhausted());
 
-  std::span<const std::byte> encoded;
-  EXPECT_EQ(encoder.Encode(&encoded), Status::ResourceExhausted());
-  EXPECT_EQ(encoded.size(), 0u);
+  ASSERT_EQ(encoder.Encode().status(), Status::ResourceExhausted());
 }
 
 TEST(Encoder, EncodeInvalidArguments) {
@@ -133,9 +133,7 @@
   encoder.Clear();
 
   EXPECT_EQ(encoder.WriteBool(19091, false), Status::InvalidArgument());
-  std::span<const std::byte> encoded;
-  EXPECT_EQ(encoder.Encode(&encoded), Status::InvalidArgument());
-  EXPECT_EQ(encoded.size(), 0u);
+  ASSERT_EQ(encoder.Encode().status(), Status::InvalidArgument());
 }
 
 TEST(Encoder, Nested) {
@@ -214,10 +212,12 @@
   };
   // clang-format on
 
-  std::span<const std::byte> encoded;
-  EXPECT_EQ(encoder.Encode(&encoded), Status::Ok());
-  EXPECT_EQ(encoded.size(), sizeof(encoded_proto));
-  EXPECT_EQ(std::memcmp(encoded.data(), encoded_proto, encoded.size()), 0);
+  Result result = encoder.Encode();
+  ASSERT_EQ(result.status(), Status::Ok());
+  EXPECT_EQ(result.value().size(), sizeof(encoded_proto));
+  EXPECT_EQ(
+      std::memcmp(result.value().data(), encoded_proto, sizeof(encoded_proto)),
+      0);
 }
 
 TEST(Encoder, NestedDepthLimit) {
@@ -274,10 +274,12 @@
   constexpr uint8_t encoded_proto[] = {
       0x08, 0x00, 0x08, 0x32, 0x08, 0x64, 0x08, 0x96, 0x01, 0x08, 0xc8, 0x01};
 
-  std::span<const std::byte> encoded;
-  EXPECT_EQ(encoder.Encode(&encoded), Status::Ok());
-  EXPECT_EQ(encoded.size(), sizeof(encoded_proto));
-  EXPECT_EQ(std::memcmp(encoded.data(), encoded_proto, encoded.size()), 0);
+  Result result = encoder.Encode();
+  ASSERT_EQ(result.status(), Status::Ok());
+  EXPECT_EQ(result.value().size(), sizeof(encoded_proto));
+  EXPECT_EQ(
+      std::memcmp(result.value().data(), encoded_proto, sizeof(encoded_proto)),
+      0);
 }
 
 TEST(Encoder, PackedVarint) {
@@ -292,10 +294,12 @@
       0x0a, 0x07, 0x00, 0x32, 0x64, 0x96, 0x01, 0xc8, 0x01};
   //  key   size  v[0]  v[1]  v[2]  v[3]        v[4]
 
-  std::span<const std::byte> encoded;
-  EXPECT_EQ(encoder.Encode(&encoded), Status::Ok());
-  EXPECT_EQ(encoded.size(), sizeof(encoded_proto));
-  EXPECT_EQ(std::memcmp(encoded.data(), encoded_proto, encoded.size()), 0);
+  Result result = encoder.Encode();
+  ASSERT_EQ(result.status(), Status::Ok());
+  EXPECT_EQ(result.value().size(), sizeof(encoded_proto));
+  EXPECT_EQ(
+      std::memcmp(result.value().data(), encoded_proto, sizeof(encoded_proto)),
+      0);
 }
 
 TEST(Encoder, PackedVarintInsufficientSpace) {
@@ -305,9 +309,7 @@
   constexpr uint32_t values[] = {0, 50, 100, 150, 200};
   encoder.WritePackedUint32(1, values);
 
-  std::span<const std::byte> encoded;
-  EXPECT_EQ(encoder.Encode(&encoded), Status::ResourceExhausted());
-  EXPECT_EQ(encoded.size(), 0u);
+  EXPECT_EQ(encoder.Encode().status(), Status::ResourceExhausted());
 }
 
 TEST(Encoder, PackedFixed) {
@@ -327,10 +329,12 @@
       0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00,
       0x12, 0x08, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01};
 
-  std::span<const std::byte> encoded;
-  EXPECT_EQ(encoder.Encode(&encoded), Status::Ok());
-  EXPECT_EQ(encoded.size(), sizeof(encoded_proto));
-  EXPECT_EQ(std::memcmp(encoded.data(), encoded_proto, encoded.size()), 0);
+  Result result = encoder.Encode();
+  ASSERT_EQ(result.status(), Status::Ok());
+  EXPECT_EQ(result.value().size(), sizeof(encoded_proto));
+  EXPECT_EQ(
+      std::memcmp(result.value().data(), encoded_proto, sizeof(encoded_proto)),
+      0);
 }
 
 TEST(Encoder, PackedZigzag) {
@@ -344,10 +348,12 @@
   constexpr uint8_t encoded_proto[] = {
       0x0a, 0x09, 0xc7, 0x01, 0x31, 0x01, 0x00, 0x02, 0x32, 0xc8, 0x01};
 
-  std::span<const std::byte> encoded;
-  EXPECT_EQ(encoder.Encode(&encoded), Status::Ok());
-  EXPECT_EQ(encoded.size(), sizeof(encoded_proto));
-  EXPECT_EQ(std::memcmp(encoded.data(), encoded_proto, encoded.size()), 0);
+  Result result = encoder.Encode();
+  ASSERT_EQ(result.status(), Status::Ok());
+  EXPECT_EQ(result.value().size(), sizeof(encoded_proto));
+  EXPECT_EQ(
+      std::memcmp(result.value().data(), encoded_proto, sizeof(encoded_proto)),
+      0);
 }
 
 }  // namespace
diff --git a/pw_protobuf/public/pw_protobuf/encoder.h b/pw_protobuf/public/pw_protobuf/encoder.h
index 14483d2..c859009 100644
--- a/pw_protobuf/public/pw_protobuf/encoder.h
+++ b/pw_protobuf/public/pw_protobuf/encoder.h
@@ -17,7 +17,9 @@
 #include <cstring>
 #include <span>
 
+#include "pw_bytes/span.h"
 #include "pw_protobuf/wire_format.h"
+#include "pw_result/result.h"
 #include "pw_status/status.h"
 #include "pw_varint/varint.h"
 
@@ -31,7 +33,7 @@
   // message. This can be templated to minimize the overhead.
   using SizeType = size_t;
 
-  constexpr Encoder(std::span<std::byte> buffer,
+  constexpr Encoder(ByteSpan buffer,
                     std::span<SizeType*> locations,
                     std::span<SizeType*> stack)
       : buffer_(buffer),
@@ -225,7 +227,7 @@
   }
 
   // Writes a proto bytes key-value pair.
-  Status WriteBytes(uint32_t field_number, std::span<const std::byte> value) {
+  Status WriteBytes(uint32_t field_number, ConstByteSpan value) {
     std::byte* original_cursor = cursor_;
     WriteFieldKey(field_number, WireType::kDelimited);
     WriteVarint(value.size_bytes());
@@ -266,7 +268,18 @@
 
   // Runs a final encoding pass over the intermediary data and returns the
   // encoded protobuf message.
-  Status Encode(std::span<const std::byte>* out);
+  Result<ConstByteSpan> Encode();
+
+  // DEPRECATED. Use Encode() instead.
+  // TODO(frolv): Remove this after all references to it are updated.
+  Status Encode(ConstByteSpan* out) {
+    Result result = Encode();
+    if (!result.ok()) {
+      return result.status();
+    }
+    *out = result.value();
+    return Status::Ok();
+  }
 
  private:
   constexpr bool ValidFieldNumber(uint32_t field_number) const {
@@ -340,7 +353,7 @@
   }
 
   // The buffer into which the proto is encoded.
-  std::span<std::byte> buffer_;
+  ByteSpan buffer_;
   std::byte* cursor_;
 
   // List of pointers to sub-messages' delimiting size fields.
@@ -359,8 +372,7 @@
 template <size_t kMaxNestedDepth = 1, size_t kMaxBlobs = 1>
 class NestedEncoder : public Encoder {
  public:
-  NestedEncoder(std::span<std::byte> buffer)
-      : Encoder(buffer, blobs_, stack_) {}
+  NestedEncoder(ByteSpan buffer) : Encoder(buffer, blobs_, stack_) {}
 
   // Disallow copy/assign to avoid confusion about who owns the buffer.
   NestedEncoder(const NestedEncoder& other) = delete;
diff --git a/pw_rpc/packet.cc b/pw_rpc/packet.cc
index 56969ca..d99cd84 100644
--- a/pw_rpc/packet.cc
+++ b/pw_rpc/packet.cc
@@ -80,12 +80,12 @@
   rpc_packet.WriteMethodId(method_id_);
   rpc_packet.WriteStatus(status_);
 
-  std::span<const byte> proto;
-  if (Status status = encoder.Encode(&proto); !status.ok()) {
-    return StatusWithSize(status, 0);
+  Result result = encoder.Encode();
+  if (!result.ok()) {
+    return StatusWithSize(result.status(), 0);
   }
 
-  return StatusWithSize(proto.size());
+  return StatusWithSize(result.value().size());
 }
 
 size_t Packet::MinEncodedSizeBytes() const {