pw_protobuf: Generate enum-to-string functions
Generate an <EnumName>ToString() function alongside the enum and the
IsValid<EnumName>() function.
Change-Id: I7b9a8c3cb9e24321072a32821430f8ec9aff9aeb
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/126683
Commit-Queue: Wyatt Hepler <hepler@google.com>
Reviewed-by: Alexei Frolov <frolv@google.com>
Pigweed-Auto-Submit: Wyatt Hepler <hepler@google.com>
diff --git a/pw_protobuf/codegen_encoder_test.cc b/pw_protobuf/codegen_encoder_test.cc
index e6645a9..de0a581 100644
--- a/pw_protobuf/codegen_encoder_test.cc
+++ b/pw_protobuf/codegen_encoder_test.cc
@@ -551,5 +551,26 @@
0);
}
+TEST(Codegen, EnumToString) {
+ EXPECT_STREQ(test::pwpb::BoolToString(test::pwpb::Bool::kTrue), "TRUE");
+ EXPECT_STREQ(test::pwpb::BoolToString(test::pwpb::Bool::kFalse), "FALSE");
+ EXPECT_STREQ(test::pwpb::BoolToString(test::pwpb::Bool::kFileNotFound),
+ "FILE_NOT_FOUND");
+ EXPECT_STREQ(test::pwpb::BoolToString(static_cast<test::pwpb::Bool>(12893)),
+ "");
+}
+
+TEST(Codegen, NestedEnumToString) {
+ EXPECT_STREQ(test::pwpb::Pigweed::Pigweed::BinaryToString(
+ test::pwpb::Pigweed::Pigweed::Binary::kZero),
+ "ZERO");
+ EXPECT_STREQ(test::pwpb::Pigweed::Pigweed::BinaryToString(
+ test::pwpb::Pigweed::Pigweed::Binary::kOne),
+ "ONE");
+ EXPECT_STREQ(test::pwpb::Pigweed::Pigweed::BinaryToString(
+ static_cast<test::pwpb::Pigweed::Pigweed::Binary>(12893)),
+ "");
+}
+
} // namespace
} // namespace pw::protobuf
diff --git a/pw_protobuf/docs.rst b/pw_protobuf/docs.rst
index 787f150..7ebfb14 100644
--- a/pw_protobuf/docs.rst
+++ b/pw_protobuf/docs.rst
@@ -1723,15 +1723,24 @@
Enumerations
============
-Enumerations are read using code generated ``ReadEnum`` methods that return the
-code generated enumeration as the appropriate type.
+``pw_protobuf`` generates a few functions for working with enumerations.
+Most importantly, enumerations are read using generated ``ReadEnum`` methods
+that return the enumeration as the appropriate generated type.
.. cpp:function:: Result<MyProto::Enum> MyProto::StreamDecoder::ReadEnum()
-To validate the value encoded in the wire format against the known set of
-enumerates, a function is generated that you can use:
+ Decodes an enum from the stream.
-.. cpp:function:: bool MyProto::IsValidEnum(MyProto::Enum)
+.. cpp:function:: constexpr bool MyProto::IsValidEnum(MyProto::Enum value)
+
+ Validates the value encoded in the wire format against the known set of
+ enumerates.
+
+.. cpp:function:: constexpr const char* MyProto::EnumToString(MyProto::Enum value)
+
+ Returns the string representation of the enum value. For example,
+ ``FooToString(Foo::kBarBaz)`` returns ``"BAR_BAZ"``. Returns the empty string
+ if the value is not a valid value.
To read enumerations with the lower-level API, you would need to cast the
retured value from the ``uint32_t``.
@@ -1739,14 +1748,14 @@
The following two code blocks are equivalent, where the first is using the code
generated API, and the second implemented by hand.
-.. code:: c++
+.. code-block:: c++
pw::Result<MyProto::Award> award = my_proto_decoder.ReadAward();
if (!MyProto::IsValidAward(award)) {
PW_LOG_DBG("Unknown award");
}
-.. code:: c++
+.. code-block:: c++
PW_ASSERT(my_proto_decoder.FieldNumber().value() ==
static_cast<uint32_t>(MyProto::Fields::AWARD));
diff --git a/pw_protobuf/py/pw_protobuf/codegen_pwpb.py b/pw_protobuf/py/pw_protobuf/codegen_pwpb.py
index 3945dbc..2a3346b 100644
--- a/pw_protobuf/py/pw_protobuf/codegen_pwpb.py
+++ b/pw_protobuf/py/pw_protobuf/codegen_pwpb.py
@@ -2273,7 +2273,7 @@
def generate_function_for_enum(
proto_enum: ProtoEnum, root: ProtoNode, output: OutputFile
) -> None:
- """Creates a C++ validation function for for a proto enum."""
+ """Creates a C++ validation function for a proto enum."""
assert proto_enum.type() == ProtoNode.Type.ENUM
enum_name = proto_enum.cpp_namespace(root=root)
@@ -2290,6 +2290,30 @@
output.write_line('}')
+def generate_to_string_for_enum(
+ proto_enum: ProtoEnum, root: ProtoNode, output: OutputFile
+) -> None:
+ """Creates a C++ to string function for a proto enum."""
+ assert proto_enum.type() == ProtoNode.Type.ENUM
+
+ enum_name = proto_enum.cpp_namespace(root=root)
+ output.write_line(
+ f'// Returns string names for {enum_name}; '
+ 'returns "" for invalid enum values.'
+ )
+ output.write_line(
+ f'constexpr const char* {enum_name}ToString({enum_name} value) {{'
+ )
+ with output.indent():
+ output.write_line('switch (value) {')
+ with output.indent():
+ for name, _ in proto_enum.values():
+ output.write_line(f'case {enum_name}::{name}: return "{name}";')
+ output.write_line('default: return "";')
+ output.write_line('}')
+ output.write_line('}')
+
+
def forward_declare(
node: ProtoMessage, root: ProtoNode, output: OutputFile
) -> None:
@@ -2325,6 +2349,8 @@
generate_code_for_enum(cast(ProtoEnum, child), node, output)
output.write_line()
generate_function_for_enum(cast(ProtoEnum, child), node, output)
+ output.write_line()
+ generate_to_string_for_enum(cast(ProtoEnum, child), node, output)
output.write_line(f'}} // namespace {namespace}')
@@ -2576,6 +2602,8 @@
generate_code_for_enum(cast(ProtoEnum, node), package, output)
output.write_line()
generate_function_for_enum(cast(ProtoEnum, node), package, output)
+ output.write_line()
+ generate_to_string_for_enum(cast(ProtoEnum, node), package, output)
# Run through all messages, generating structs and classes for each.
messages = []