From 230232a041d7bce0080f459512ee2162ee9233e7 Mon Sep 17 00:00:00 2001 From: pizzud Date: Mon, 21 Aug 2023 11:41:54 -0700 Subject: [PATCH] Implement AbslStringify for the Descriptor family of types. Implementing this method allows descriptors to be used in contexts that support ABSL's formatting, most notably gunit's PrintToString and absl's StrFormat. The implementations are done on the concrete descriptor type, which appears to be more idiomatic for AbslStringify. Implementing them on pointers-to-descriptors would be nicer for users who typically interact with pointers, but that would conflict with AbslStringify's built-in support for pointers. The implementation simply forwards to DebugString for simplicity. PiperOrigin-RevId: 558854354 --- src/google/protobuf/descriptor.h | 49 ++++++++++++++++++++++ src/google/protobuf/descriptor_unittest.cc | 29 +++++++++++++ 2 files changed, 78 insertions(+) diff --git a/src/google/protobuf/descriptor.h b/src/google/protobuf/descriptor.h index 5cd1c1e35d..fd31e37db2 100644 --- a/src/google/protobuf/descriptor.h +++ b/src/google/protobuf/descriptor.h @@ -69,6 +69,7 @@ #include "absl/container/flat_hash_map.h" #include "absl/log/absl_check.h" #include "absl/log/absl_log.h" +#include "absl/strings/str_format.h" #include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" #include "absl/types/optional.h" @@ -347,6 +348,12 @@ class PROTOBUF_EXPORT Descriptor : private internal::SymbolBase { // include original user comments in output). std::string DebugStringWithOptions(const DebugStringOptions& options) const; + // Allows formatting with absl and gtest. + template + friend void AbslStringify(Sink& sink, const Descriptor& d) { + absl::Format(&sink, "%s", d.DebugString()); + } + // Returns true if this is a placeholder for an unknown type. This will // only be the case if this descriptor comes from a DescriptorPool // with AllowUnknownDependencies() set. @@ -981,6 +988,12 @@ class PROTOBUF_EXPORT FieldDescriptor : private internal::SymbolBase { // See Descriptor::DebugStringWithOptions(). std::string DebugStringWithOptions(const DebugStringOptions& options) const; + // Allows formatting with absl and gtest. + template + friend void AbslStringify(Sink& sink, const FieldDescriptor& d) { + absl::Format(&sink, "%s", d.DebugString()); + } + // Helper method to get the CppType for a particular Type. static CppType TypeToCppType(Type type); @@ -1186,6 +1199,12 @@ class PROTOBUF_EXPORT OneofDescriptor : private internal::SymbolBase { // See Descriptor::DebugStringWithOptions(). std::string DebugStringWithOptions(const DebugStringOptions& options) const; + // Allows formatting with absl and gtest. + template + friend void AbslStringify(Sink& sink, const OneofDescriptor& d) { + absl::Format(&sink, "%s", d.DebugString()); + } + // Source Location --------------------------------------------------- // Updates |*out_location| to the source location of the complete @@ -1307,6 +1326,12 @@ class PROTOBUF_EXPORT EnumDescriptor : private internal::SymbolBase { // See Descriptor::DebugStringWithOptions(). std::string DebugStringWithOptions(const DebugStringOptions& options) const; + // Allows formatting with absl and gtest. + template + friend void AbslStringify(Sink& sink, const EnumDescriptor& d) { + absl::Format(&sink, "%s", d.DebugString()); + } + // Returns true if this is a placeholder for an unknown enum. This will // only be the case if this descriptor comes from a DescriptorPool // with AllowUnknownDependencies() set. @@ -1500,6 +1525,12 @@ class PROTOBUF_EXPORT EnumValueDescriptor : private internal::SymbolBaseN<0>, // See Descriptor::DebugStringWithOptions(). std::string DebugStringWithOptions(const DebugStringOptions& options) const; + // Allows formatting with absl and gtest. + template + friend void AbslStringify(Sink& sink, const EnumValueDescriptor& d) { + absl::Format(&sink, "%s", d.DebugString()); + } + // Source Location --------------------------------------------------- // Updates |*out_location| to the source location of the complete @@ -1597,6 +1628,12 @@ class PROTOBUF_EXPORT ServiceDescriptor : private internal::SymbolBase { // See Descriptor::DebugStringWithOptions(). std::string DebugStringWithOptions(const DebugStringOptions& options) const; + // Allows formatting with absl and gtest. + template + friend void AbslStringify(Sink& sink, const ServiceDescriptor& d) { + absl::Format(&sink, "%s", d.DebugString()); + } + // Source Location --------------------------------------------------- // Updates |*out_location| to the source location of the complete @@ -1698,6 +1735,12 @@ class PROTOBUF_EXPORT MethodDescriptor : private internal::SymbolBase { // See Descriptor::DebugStringWithOptions(). std::string DebugStringWithOptions(const DebugStringOptions& options) const; + // Allows formatting with absl and gtest. + template + friend void AbslStringify(Sink& sink, const MethodDescriptor& d) { + absl::Format(&sink, "%s", d.DebugString()); + } + // Source Location --------------------------------------------------- // Updates |*out_location| to the source location of the complete @@ -1913,6 +1956,12 @@ class PROTOBUF_EXPORT FileDescriptor : private internal::SymbolBase { // See Descriptor::DebugStringWithOptions(). std::string DebugStringWithOptions(const DebugStringOptions& options) const; + // Allows formatting with absl and gtest. + template + friend void AbslStringify(Sink& sink, const FileDescriptor& d) { + absl::Format(&sink, "%s", d.DebugString()); + } + // Returns true if this is a placeholder for an unknown file. This will // only be the case if this descriptor comes from a DescriptorPool // with AllowUnknownDependencies() set. diff --git a/src/google/protobuf/descriptor_unittest.cc b/src/google/protobuf/descriptor_unittest.cc index e444de63d8..548c7bc5bc 100644 --- a/src/google/protobuf/descriptor_unittest.cc +++ b/src/google/protobuf/descriptor_unittest.cc @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -83,6 +84,7 @@ using ::testing::AnyOf; using ::testing::ElementsAre; +using ::testing::HasSubstr; using ::testing::NotNull; namespace google { @@ -614,6 +616,13 @@ TEST_F(FileDescriptorTest, DebugStringRoundTrip) { } } +TEST_F(FileDescriptorTest, AbslStringifyWorks) { + std::string s = absl::StrFormat( + "%v", + *protobuf_unittest::TestMessageWithCustomOptions::descriptor()->file()); + EXPECT_THAT(s, HasSubstr("TestMessageWithCustomOptions")); +} + // =================================================================== // Test simple flat messages and fields. @@ -1209,6 +1218,12 @@ TEST_F(DescriptorTest, FieldEnumType) { EXPECT_EQ(enum_, bar_->enum_type()); } +TEST_F(DescriptorTest, AbslStringifyWorks) { + EXPECT_THAT(absl::StrFormat("%v", *message_), + HasSubstr(message_->full_name())); + EXPECT_THAT(absl::StrFormat("%v", *foo_), HasSubstr(foo_->name())); +} + // =================================================================== @@ -1302,6 +1317,10 @@ TEST_F(OneofDescriptorTest, FindByName) { EXPECT_TRUE(oneof_message_->FindOneofByName("no_such_oneof") == nullptr); } +TEST_F(OneofDescriptorTest, AbslStringifyWorks) { + EXPECT_THAT(absl::StrFormat("%v", *oneof_), HasSubstr(oneof_->name())); +} + // =================================================================== class StylizedFieldNamesTest : public testing::Test { @@ -1650,6 +1669,11 @@ TEST_F(EnumDescriptorTest, IsClosed) { EXPECT_FALSE(enum3->is_closed()); } +TEST_F(EnumDescriptorTest, AbslStringifyWorks) { + EXPECT_THAT(absl::StrFormat("%v", *enum_), HasSubstr(enum_->full_name())); + EXPECT_THAT(absl::StrFormat("%v", *foo_), HasSubstr(foo_->name())); +} + // =================================================================== // Test service descriptors. @@ -1810,6 +1834,11 @@ TEST_F(ServiceDescriptorTest, MethodOutputType) { EXPECT_EQ(bar_response_, bar_->output_type()); } +TEST_F(ServiceDescriptorTest, AbslStringifyWorks) { + EXPECT_THAT(absl::StrFormat("%v", *service_), HasSubstr(service_->name())); + EXPECT_THAT(absl::StrFormat("%v", *foo_), HasSubstr(foo_->name())); +} + // =================================================================== // Test nested types.