pw_protobuf: Add codegen for typed StreamDecoder::Field()
Since the codegen StreamDecoder knows what type its Fields enum is, add
a Field() accessor that converts FieldNumber() into the correct type.
Change-Id: I5b889b6a61455984f14a5e4d9b97e1278ad2995e
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/88407
Reviewed-by: Armando Montanez <amontanez@google.com>
Commit-Queue: Scott James Remnant <keybuk@google.com>
diff --git a/pw_protobuf/codegen_decoder_test.cc b/pw_protobuf/codegen_decoder_test.cc
index 2602250..fd7ed8f 100644
--- a/pw_protobuf/codegen_decoder_test.cc
+++ b/pw_protobuf/codegen_decoder_test.cc
@@ -121,11 +121,13 @@
Pigweed::StreamDecoder pigweed(reader);
EXPECT_EQ(pigweed.Next(), OkStatus());
+ EXPECT_EQ(pigweed.Field().value(), Pigweed::Fields::MAGIC_NUMBER);
Result<uint32_t> magic_number = pigweed.ReadMagicNumber();
EXPECT_EQ(magic_number.status(), OkStatus());
EXPECT_EQ(magic_number.value(), 0x49u);
EXPECT_EQ(pigweed.Next(), OkStatus());
+ EXPECT_EQ(pigweed.Field().value(), Pigweed::Fields::ZIGGY);
Result<int32_t> ziggy = pigweed.ReadZiggy();
EXPECT_EQ(ziggy.status(), OkStatus());
EXPECT_EQ(ziggy.value(), -111);
@@ -133,6 +135,7 @@
constexpr std::string_view kExpectedErrorMessage{"not a typewriter"};
EXPECT_EQ(pigweed.Next(), OkStatus());
+ EXPECT_EQ(pigweed.Field().value(), Pigweed::Fields::ERROR_MESSAGE);
std::array<char, 32> error_message{};
StatusWithSize error_message_status = pigweed.ReadErrorMessage(error_message);
EXPECT_EQ(error_message_status.status(), OkStatus());
@@ -143,16 +146,20 @@
0);
EXPECT_EQ(pigweed.Next(), OkStatus());
+ EXPECT_EQ(pigweed.Field().value(), Pigweed::Fields::BIN);
Result<Pigweed::Protobuf::Binary> bin = pigweed.ReadBin();
EXPECT_EQ(bin.status(), OkStatus());
EXPECT_EQ(bin.value(), Pigweed::Protobuf::Binary::ZERO);
EXPECT_EQ(pigweed.Next(), OkStatus());
+ EXPECT_EQ(pigweed.Field().value(), Pigweed::Fields::PIGWEED);
{
Pigweed::Pigweed::StreamDecoder pigweed_pigweed =
pigweed.GetPigweedDecoder();
EXPECT_EQ(pigweed_pigweed.Next(), OkStatus());
+ EXPECT_EQ(pigweed_pigweed.Field().value(),
+ Pigweed::Pigweed::Fields::STATUS);
Result<Bool> pigweed_status = pigweed_pigweed.ReadStatus();
EXPECT_EQ(pigweed_status.status(), OkStatus());
EXPECT_EQ(pigweed_status.value(), Bool::FILE_NOT_FOUND);
@@ -161,33 +168,40 @@
}
EXPECT_EQ(pigweed.Next(), OkStatus());
+ EXPECT_EQ(pigweed.Field().value(), Pigweed::Fields::PROTO);
{
Proto::StreamDecoder proto = pigweed.GetProtoDecoder();
EXPECT_EQ(proto.Next(), OkStatus());
+ EXPECT_EQ(proto.Field().value(), Proto::Fields::BIN);
Result<Proto::Binary> proto_bin = proto.ReadBin();
EXPECT_EQ(proto_bin.status(), OkStatus());
EXPECT_EQ(proto_bin.value(), Proto::Binary::OFF);
EXPECT_EQ(proto.Next(), OkStatus());
+ EXPECT_EQ(proto.Field().value(), Proto::Fields::PIGWEED_PIGWEED_BIN);
Result<Pigweed::Pigweed::Binary> proto_pigweed_bin =
proto.ReadPigweedPigweedBin();
EXPECT_EQ(proto_pigweed_bin.status(), OkStatus());
EXPECT_EQ(proto_pigweed_bin.value(), Pigweed::Pigweed::Binary::ZERO);
EXPECT_EQ(proto.Next(), OkStatus());
+ EXPECT_EQ(proto.Field().value(), Proto::Fields::PIGWEED_PROTOBUF_BIN);
Result<Pigweed::Protobuf::Binary> proto_protobuf_bin =
proto.ReadPigweedProtobufBin();
EXPECT_EQ(proto_protobuf_bin.status(), OkStatus());
EXPECT_EQ(proto_protobuf_bin.value(), Pigweed::Protobuf::Binary::ZERO);
EXPECT_EQ(proto.Next(), OkStatus());
+ EXPECT_EQ(proto.Field().value(), Proto::Fields::META);
{
Pigweed::Protobuf::Compiler::StreamDecoder meta = proto.GetMetaDecoder();
constexpr std::string_view kExpectedFileName{"/etc/passwd"};
EXPECT_EQ(meta.Next(), OkStatus());
+ EXPECT_EQ(meta.Field().value(),
+ Pigweed::Protobuf::Compiler::Fields::FILE_NAME);
std::array<char, 32> meta_file_name{};
StatusWithSize meta_file_name_status = meta.ReadFileName(meta_file_name);
EXPECT_EQ(meta_file_name_status.status(), OkStatus());
@@ -198,6 +212,8 @@
0);
EXPECT_EQ(meta.Next(), OkStatus());
+ EXPECT_EQ(meta.Field().value(),
+ Pigweed::Protobuf::Compiler::Fields::STATUS);
Result<Pigweed::Protobuf::Compiler::Status> meta_status =
meta.ReadStatus();
EXPECT_EQ(meta_status.status(), OkStatus());
@@ -208,12 +224,14 @@
}
EXPECT_EQ(proto.Next(), OkStatus());
+ EXPECT_EQ(proto.Field().value(), Proto::Fields::PIGWEED);
{
Pigweed::StreamDecoder proto_pigweed = proto.GetPigweedDecoder();
constexpr std::string_view kExpectedProtoErrorMessage{"here we go again"};
EXPECT_EQ(proto_pigweed.Next(), OkStatus());
+ EXPECT_EQ(proto_pigweed.Field().value(), Pigweed::Fields::ERROR_MESSAGE);
std::array<char, 32> proto_pigweed_error_message{};
StatusWithSize proto_pigweed_error_message_status =
proto_pigweed.ReadErrorMessage(proto_pigweed_error_message);
@@ -226,17 +244,20 @@
0);
EXPECT_EQ(proto_pigweed.Next(), OkStatus());
+ EXPECT_EQ(proto_pigweed.Field().value(), Pigweed::Fields::MAGIC_NUMBER);
Result<uint32_t> proto_pigweed_magic_number =
proto_pigweed.ReadMagicNumber();
EXPECT_EQ(proto_pigweed_magic_number.status(), OkStatus());
EXPECT_EQ(proto_pigweed_magic_number.value(), 616u);
EXPECT_EQ(proto_pigweed.Next(), OkStatus());
+ EXPECT_EQ(proto_pigweed.Field().value(), Pigweed::Fields::DEVICE_INFO);
{
DeviceInfo::StreamDecoder device_info =
proto_pigweed.GetDeviceInfoDecoder();
EXPECT_EQ(device_info.Next(), OkStatus());
+ EXPECT_EQ(device_info.Field().value(), DeviceInfo::Fields::ATTRIBUTES);
{
KeyValuePair::StreamDecoder key_value_pair =
device_info.GetAttributesDecoder();
@@ -245,6 +266,7 @@
constexpr std::string_view kExpectedValue{"5.3.1"};
EXPECT_EQ(key_value_pair.Next(), OkStatus());
+ EXPECT_EQ(key_value_pair.Field().value(), KeyValuePair::Fields::KEY);
std::array<char, 32> key{};
StatusWithSize key_status = key_value_pair.ReadKey(key);
EXPECT_EQ(key_status.status(), OkStatus());
@@ -254,6 +276,8 @@
0);
EXPECT_EQ(key_value_pair.Next(), OkStatus());
+ EXPECT_EQ(key_value_pair.Field().value(),
+ KeyValuePair::Fields::VALUE);
std::array<char, 32> value{};
StatusWithSize value_status = key_value_pair.ReadValue(value);
EXPECT_EQ(value_status.status(), OkStatus());
@@ -267,6 +291,7 @@
}
EXPECT_EQ(device_info.Next(), OkStatus());
+ EXPECT_EQ(device_info.Field().value(), DeviceInfo::Fields::ATTRIBUTES);
{
KeyValuePair::StreamDecoder key_value_pair =
device_info.GetAttributesDecoder();
@@ -275,6 +300,7 @@
constexpr std::string_view kExpectedValue{"left-soc"};
EXPECT_EQ(key_value_pair.Next(), OkStatus());
+ EXPECT_EQ(key_value_pair.Field().value(), KeyValuePair::Fields::KEY);
std::array<char, 32> key{};
StatusWithSize key_status = key_value_pair.ReadKey(key);
EXPECT_EQ(key_status.status(), OkStatus());
@@ -284,6 +310,8 @@
0);
EXPECT_EQ(key_value_pair.Next(), OkStatus());
+ EXPECT_EQ(key_value_pair.Field().value(),
+ KeyValuePair::Fields::VALUE);
std::array<char, 32> value{};
StatusWithSize value_status = key_value_pair.ReadValue(value);
EXPECT_EQ(value_status.status(), OkStatus());
@@ -297,6 +325,7 @@
}
EXPECT_EQ(device_info.Next(), OkStatus());
+ EXPECT_EQ(device_info.Field().value(), DeviceInfo::Fields::STATUS);
Result<DeviceInfo::DeviceStatus> device_info_status =
device_info.ReadStatus();
EXPECT_EQ(device_info_status.status(), OkStatus());
@@ -313,10 +342,12 @@
for (int i = 0; i < 5; ++i) {
EXPECT_EQ(pigweed.Next(), OkStatus());
+ EXPECT_EQ(pigweed.Field().value(), Pigweed::Fields::ID);
Proto::ID::StreamDecoder id = pigweed.GetIdDecoder();
EXPECT_EQ(id.Next(), OkStatus());
+ EXPECT_EQ(id.Field().value(), Proto::ID::Fields::ID);
Result<uint32_t> id_id = id.ReadId();
EXPECT_EQ(id_id.status(), OkStatus());
EXPECT_EQ(id_id.value(), 5u * i * i + 3 * i + 49);
@@ -340,6 +371,7 @@
Pigweed::StreamDecoder pigweed(reader);
EXPECT_EQ(pigweed.Next(), OkStatus());
+ EXPECT_EQ(pigweed.Field().value(), Pigweed::Fields::ERROR_MESSAGE);
std::array<char, 8> error_message{};
StatusWithSize error_message_status = pigweed.ReadErrorMessage(error_message);
EXPECT_EQ(error_message_status.status(), Status::ResourceExhausted());
@@ -363,6 +395,7 @@
constexpr std::string_view kExpectedErrorMessage{"not a typewriter"};
EXPECT_EQ(pigweed.Next(), OkStatus());
+ EXPECT_EQ(pigweed.Field().value(), Pigweed::Fields::ERROR_MESSAGE);
{
StreamDecoder::BytesReader bytes_reader = pigweed.GetErrorMessageReader();
EXPECT_EQ(bytes_reader.field_size(), kExpectedErrorMessage.size());
diff --git a/pw_protobuf/docs.rst b/pw_protobuf/docs.rst
index 6de93e5..56675eb 100644
--- a/pw_protobuf/docs.rst
+++ b/pw_protobuf/docs.rst
@@ -285,20 +285,27 @@
pw::stream::SysIoReader sys_io_reader;
// The constructor is the same as a pw::protobuf::StreamDecoder.
fuzzy_friends::Client::StreamDecoder client(sys_io_reader);
- {
- std::array<char, 32> name{};
- std::array<char, 32> pet_type{};
- fuzzy_friends::Pet::StreamDecoder pet1 = client.GetPetsDecoder();
- pet1.ReadName(name);
- pet1.ReadPetType(pet_type);
- }
+ while (client.Next().ok()) {
+ switch (client.Field().value) {
+ case fuzzy_friends::Client::Fields::PET: {
+ std::array<char, 32> name{};
+ std::array<char, 32> pet_type{};
- {
- std::array<char, 32> name{};
- std::array<char, 32> pet_type{};
- fuzzy_friends::Pet::StreamDecoder pet2 = client.GetPetsDecoder();
- pet2.ReadName(name);
- pet2.ReadPetType(pet_type);
+ fuzzy_friends::Pet::StreamDecoder pet = client.GetPetsDecoder();
+ while (pet.Next().ok()) {
+ switch (pet.Field().value) {
+ case fuzzy_friends::Pet::NAME:
+ pet.ReadName(name);
+ break;
+ case fuzzy_friends::Pet::TYPE:
+ pet.ReadPetType(pet_type);
+ break;
+ }
+ }
+
+ break;
+ }
+ }
}
if (!client.status().ok()) {
diff --git a/pw_protobuf/py/pw_protobuf/codegen_pwpb.py b/pw_protobuf/py/pw_protobuf/codegen_pwpb.py
index 69a728b..4743e49 100644
--- a/pw_protobuf/py/pw_protobuf/codegen_pwpb.py
+++ b/pw_protobuf/py/pw_protobuf/codegen_pwpb.py
@@ -880,6 +880,21 @@
f'*static_cast<{PROTOBUF_NAMESPACE}::StreamEncoder*>(this));}}'
)
+ # Add a typed Field() member to StreamDecoder
+ if class_type == ClassType.STREAMING_DECODER:
+ output.write_line()
+ output.write_line('::pw::Result<Fields> Field() {')
+ with output.indent():
+ output.write_line('::pw::Result<uint32_t> result '
+ '= FieldNumber();')
+ output.write_line('if (!result.ok()) {')
+ with output.indent():
+ output.write_line('return result.status();')
+ output.write_line('}')
+ output.write_line(
+ 'return static_cast<Fields>(result.value());')
+ output.write_line('}')
+
# Generate methods for each of the message's fields.
for field in message.fields():
for method_class in proto_field_methods(class_type, field.type()):