diff --git a/src/google/protobuf/compiler/rust/BUILD.bazel b/src/google/protobuf/compiler/rust/BUILD.bazel index 6682c2ec20..52721ad266 100644 --- a/src/google/protobuf/compiler/rust/BUILD.bazel +++ b/src/google/protobuf/compiler/rust/BUILD.bazel @@ -120,8 +120,8 @@ cc_library( strip_include_prefix = "/src", deps = [ ":context", + ":naming", "//src/google/protobuf", - "//src/google/protobuf/compiler/cpp:names_internal", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/log:absl_check", @@ -151,6 +151,7 @@ cc_library( ":context", "//src/google/protobuf", "//src/google/protobuf/compiler:code_generator", + "//src/google/protobuf/compiler/cpp:names_internal", "@com_google_absl//absl/log:absl_log", "@com_google_absl//absl/strings", ], diff --git a/src/google/protobuf/compiler/rust/enum.cc b/src/google/protobuf/compiler/rust/enum.cc index de9382a99a..2f4b08da17 100644 --- a/src/google/protobuf/compiler/rust/enum.cc +++ b/src/google/protobuf/compiler/rust/enum.cc @@ -24,8 +24,8 @@ #include "absl/strings/string_view.h" #include "absl/strings/strip.h" #include "absl/types/span.h" -#include "google/protobuf/compiler/cpp/helpers.h" #include "google/protobuf/compiler/rust/context.h" +#include "google/protobuf/compiler/rust/naming.h" #include "google/protobuf/descriptor.h" namespace google { @@ -34,11 +34,6 @@ namespace compiler { namespace rust { namespace { - -std::string EnumName(const EnumDescriptor& desc) { - return cpp::UnderscoresToCamelCase(desc.name(), /*cap first letter=*/true); -} - // Constructs input for `EnumValues` from an enum descriptor. std::vector> EnumValuesInput( const EnumDescriptor& desc) { @@ -126,46 +121,8 @@ std::vector EnumValues( return result; } -std::string CamelToSnakeCase(absl::string_view input) { - std::string result; - result.reserve(input.size() + 4); // No reallocation for 4 _ - bool is_first_character = true; - bool last_char_was_underscore = false; - for (const char c : input) { - if (!is_first_character && absl::ascii_isupper(c) && - !last_char_was_underscore) { - result += '_'; - } - last_char_was_underscore = c == '_'; - result += absl::ascii_tolower(c); - is_first_character = false; - } - return result; -} - -std::string ScreamingSnakeToUpperCamelCase(absl::string_view input) { - std::string result; - bool cap_next_letter = true; - for (const char c : input) { - if (absl::ascii_isalpha(c)) { - if (cap_next_letter) { - result += absl::ascii_toupper(c); - } else { - result += absl::ascii_tolower(c); - } - cap_next_letter = false; - } else if (absl::ascii_isdigit(c)) { - result += c; - cap_next_letter = true; - } else { - cap_next_letter = true; - } - } - return result; -} - void GenerateEnumDefinition(Context& ctx, const EnumDescriptor& desc) { - std::string name = EnumName(desc); + std::string name = EnumRsName(desc); ABSL_CHECK(desc.value_count() > 0); std::vector values = EnumValues(desc.name(), EnumValuesInput(desc)); diff --git a/src/google/protobuf/compiler/rust/enum.h b/src/google/protobuf/compiler/rust/enum.h index 0ef2d689f7..4c04b51cb8 100644 --- a/src/google/protobuf/compiler/rust/enum.h +++ b/src/google/protobuf/compiler/rust/enum.h @@ -40,14 +40,6 @@ std::vector EnumValues( absl::string_view enum_name, absl::Span> values); -// TODO: Unify these with other case-conversion functions. - -// Converts an UpperCamel or lowerCamel string to a snake_case string. -std::string CamelToSnakeCase(absl::string_view input); - -// Converts a SCREAMING_SNAKE_CASE string to an UpperCamelCase string. -std::string ScreamingSnakeToUpperCamelCase(absl::string_view input); - } // namespace rust } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/rust/enum_test.cc b/src/google/protobuf/compiler/rust/enum_test.cc index e481c1854d..1f3202cbce 100644 --- a/src/google/protobuf/compiler/rust/enum_test.cc +++ b/src/google/protobuf/compiler/rust/enum_test.cc @@ -8,49 +8,14 @@ namespace { -using ::google::protobuf::compiler::rust::CamelToSnakeCase; using ::google::protobuf::compiler::rust::EnumValues; using ::google::protobuf::compiler::rust::RustEnumValue; -using ::google::protobuf::compiler::rust::ScreamingSnakeToUpperCamelCase; using ::testing::AllOf; using ::testing::ElementsAre; using ::testing::Eq; using ::testing::Field; using ::testing::IsEmpty; -TEST(EnumTest, CamelToSnakeCase) { - // TODO: Review this behavior. - EXPECT_EQ(CamelToSnakeCase("CamelCase"), "camel_case"); - EXPECT_EQ(CamelToSnakeCase("_CamelCase"), "_camel_case"); - EXPECT_EQ(CamelToSnakeCase("camelCase"), "camel_case"); - EXPECT_EQ(CamelToSnakeCase("Number2020"), "number2020"); - EXPECT_EQ(CamelToSnakeCase("Number_2020"), "number_2020"); - EXPECT_EQ(CamelToSnakeCase("camelCase_"), "camel_case_"); - EXPECT_EQ(CamelToSnakeCase("CamelCaseTrio"), "camel_case_trio"); - EXPECT_EQ(CamelToSnakeCase("UnderIn_Middle"), "under_in_middle"); - EXPECT_EQ(CamelToSnakeCase("Camel_Case"), "camel_case"); - EXPECT_EQ(CamelToSnakeCase("Camel__Case"), "camel__case"); - EXPECT_EQ(CamelToSnakeCase("CAMEL_CASE"), "c_a_m_e_l_c_a_s_e"); -} - -TEST(EnumTest, ScreamingSnakeToUpperCamelCase) { - // TODO: Review this behavior. - EXPECT_EQ(ScreamingSnakeToUpperCamelCase("CAMEL_CASE"), "CamelCase"); - EXPECT_EQ(ScreamingSnakeToUpperCamelCase("NUMBER2020"), "Number2020"); - EXPECT_EQ(ScreamingSnakeToUpperCamelCase("NUMBER_2020"), "Number2020"); - EXPECT_EQ(ScreamingSnakeToUpperCamelCase("FOO_4040_BAR"), "Foo4040Bar"); - EXPECT_EQ(ScreamingSnakeToUpperCamelCase("FOO_4040bar"), "Foo4040Bar"); - EXPECT_EQ(ScreamingSnakeToUpperCamelCase("_CAMEL_CASE"), "CamelCase"); - - // This function doesn't currently preserve prefix/suffix underscore, - // while CamelToSnakeCase does. - EXPECT_EQ(ScreamingSnakeToUpperCamelCase("CAMEL_CASE_"), "CamelCase"); - EXPECT_EQ(ScreamingSnakeToUpperCamelCase("camel_case"), "CamelCase"); - EXPECT_EQ(ScreamingSnakeToUpperCamelCase("CAMEL_CASE_TRIO"), "CamelCaseTrio"); - EXPECT_EQ(ScreamingSnakeToUpperCamelCase("UNDER_IN__MIDDLE"), - "UnderInMiddle"); -} - template auto EnumValue(absl::string_view name, int32_t number, Aliases aliases) { return AllOf(Field("name", &RustEnumValue::name, Eq(name)), diff --git a/src/google/protobuf/compiler/rust/naming.cc b/src/google/protobuf/compiler/rust/naming.cc index 8f6258ec23..e488bef029 100644 --- a/src/google/protobuf/compiler/rust/naming.cc +++ b/src/google/protobuf/compiler/rust/naming.cc @@ -11,6 +11,7 @@ #include #include "absl/log/absl_log.h" +#include "absl/strings/ascii.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_join.h" #include "absl/strings/str_replace.h" @@ -18,6 +19,7 @@ #include "absl/strings/string_view.h" #include "absl/strings/substitute.h" #include "google/protobuf/compiler/code_generator.h" +#include "google/protobuf/compiler/cpp/helpers.h" #include "google/protobuf/compiler/rust/context.h" #include "google/protobuf/descriptor.h" @@ -238,6 +240,71 @@ std::string FieldInfoComment(Context& ctx, const FieldDescriptor& field) { return comment; } +std::string EnumRsName(const EnumDescriptor& desc) { + return SnakeToUpperCamelCase(desc.name()); +} + +std::string OneofViewEnumRsName(const OneofDescriptor& oneof) { + return SnakeToUpperCamelCase(oneof.name()); +} + +std::string OneofMutEnumRsName(const OneofDescriptor& oneof) { + return SnakeToUpperCamelCase(oneof.name()) + "Mut"; +} + +std::string OneofCaseEnumRsName(const OneofDescriptor& oneof) { + // Note: This is the name used for the cpp Case enum, we use it for both + // the Rust Case enum as well as for the cpp case enum in the cpp thunk. + return SnakeToUpperCamelCase(oneof.name()) + "Case"; +} + +std::string OneofCaseRsName(const FieldDescriptor& oneof_field) { + return SnakeToUpperCamelCase(oneof_field.name()); +} + +std::string CamelToSnakeCase(absl::string_view input) { + std::string result; + result.reserve(input.size() + 4); // No reallocation for 4 _ + bool is_first_character = true; + bool last_char_was_underscore = false; + for (const char c : input) { + if (!is_first_character && absl::ascii_isupper(c) && + !last_char_was_underscore) { + result += '_'; + } + last_char_was_underscore = c == '_'; + result += absl::ascii_tolower(c); + is_first_character = false; + } + return result; +} + +std::string SnakeToUpperCamelCase(absl::string_view input) { + return cpp::UnderscoresToCamelCase(input, /*cap first letter=*/true); +} + +std::string ScreamingSnakeToUpperCamelCase(absl::string_view input) { + std::string result; + result.reserve(input.size()); + bool cap_next_letter = true; + for (const char c : input) { + if (absl::ascii_isalpha(c)) { + if (cap_next_letter) { + result += absl::ascii_toupper(c); + } else { + result += absl::ascii_tolower(c); + } + cap_next_letter = false; + } else if (absl::ascii_isdigit(c)) { + result += c; + cap_next_letter = true; + } else { + cap_next_letter = true; + } + } + return result; +} + } // namespace rust } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/rust/naming.h b/src/google/protobuf/compiler/rust/naming.h index 154e9024c2..306bcbb831 100644 --- a/src/google/protobuf/compiler/rust/naming.h +++ b/src/google/protobuf/compiler/rust/naming.h @@ -35,6 +35,13 @@ std::string ThunkName(Context& ctx, const Descriptor& msg, std::string PrimitiveRsTypeName(const FieldDescriptor& field); +std::string EnumRsName(const EnumDescriptor& desc); + +std::string OneofViewEnumRsName(const OneofDescriptor& oneof); +std::string OneofMutEnumRsName(const OneofDescriptor& oneof); +std::string OneofCaseEnumRsName(const OneofDescriptor& oneof); +std::string OneofCaseRsName(const FieldDescriptor& oneof_field); + std::string FieldInfoComment(Context& ctx, const FieldDescriptor& field); std::string RustModule(Context& ctx, const Descriptor& msg); @@ -42,6 +49,17 @@ std::string RustInternalModuleName(Context& ctx, const FileDescriptor& file); std::string GetCrateRelativeQualifiedPath(Context& ctx, const Descriptor& msg); +// TODO: Unify these with other case-conversion functions. + +// Converts an UpperCamel or lowerCamel string to a snake_case string. +std::string CamelToSnakeCase(absl::string_view input); + +// Converts a snake_case string to an UpperCamelCase string. +std::string SnakeToUpperCamelCase(absl::string_view input); + +// Converts a SCREAMING_SNAKE_CASE string to an UpperCamelCase string. +std::string ScreamingSnakeToUpperCamelCase(absl::string_view input); + } // namespace rust } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/rust/naming_test.cc b/src/google/protobuf/compiler/rust/naming_test.cc index a9294d00ac..f50f217e44 100644 --- a/src/google/protobuf/compiler/rust/naming_test.cc +++ b/src/google/protobuf/compiler/rust/naming_test.cc @@ -8,11 +8,13 @@ #include "google/protobuf/descriptor.h" #include "google/protobuf/io/zero_copy_stream_impl_lite.h" +using google::protobuf::compiler::rust::CamelToSnakeCase; using google::protobuf::compiler::rust::Context; using google::protobuf::compiler::rust::Kernel; using google::protobuf::compiler::rust::Options; using google::protobuf::compiler::rust::RustGeneratorContext; using google::protobuf::compiler::rust::RustInternalModuleName; +using google::protobuf::compiler::rust::ScreamingSnakeToUpperCamelCase; using google::protobuf::io::Printer; using google::protobuf::io::StringOutputStream; @@ -34,4 +36,37 @@ TEST(RustProtoNaming, RustInternalModuleName) { EXPECT_EQ(RustInternalModuleName(c, *fd), "strong__bad_slol"); } +TEST(RustProtoNaming, CamelToSnakeCase) { + // TODO: Review this behavior. + EXPECT_EQ(CamelToSnakeCase("CamelCase"), "camel_case"); + EXPECT_EQ(CamelToSnakeCase("_CamelCase"), "_camel_case"); + EXPECT_EQ(CamelToSnakeCase("camelCase"), "camel_case"); + EXPECT_EQ(CamelToSnakeCase("Number2020"), "number2020"); + EXPECT_EQ(CamelToSnakeCase("Number_2020"), "number_2020"); + EXPECT_EQ(CamelToSnakeCase("camelCase_"), "camel_case_"); + EXPECT_EQ(CamelToSnakeCase("CamelCaseTrio"), "camel_case_trio"); + EXPECT_EQ(CamelToSnakeCase("UnderIn_Middle"), "under_in_middle"); + EXPECT_EQ(CamelToSnakeCase("Camel_Case"), "camel_case"); + EXPECT_EQ(CamelToSnakeCase("Camel__Case"), "camel__case"); + EXPECT_EQ(CamelToSnakeCase("CAMEL_CASE"), "c_a_m_e_l_c_a_s_e"); +} + +TEST(RustProtoNaming, ScreamingSnakeToUpperCamelCase) { + // TODO: Review this behavior. + EXPECT_EQ(ScreamingSnakeToUpperCamelCase("CAMEL_CASE"), "CamelCase"); + EXPECT_EQ(ScreamingSnakeToUpperCamelCase("NUMBER2020"), "Number2020"); + EXPECT_EQ(ScreamingSnakeToUpperCamelCase("NUMBER_2020"), "Number2020"); + EXPECT_EQ(ScreamingSnakeToUpperCamelCase("FOO_4040_BAR"), "Foo4040Bar"); + EXPECT_EQ(ScreamingSnakeToUpperCamelCase("FOO_4040bar"), "Foo4040Bar"); + EXPECT_EQ(ScreamingSnakeToUpperCamelCase("_CAMEL_CASE"), "CamelCase"); + + // This function doesn't currently preserve prefix/suffix underscore, + // while CamelToSnakeCase does. + EXPECT_EQ(ScreamingSnakeToUpperCamelCase("CAMEL_CASE_"), "CamelCase"); + EXPECT_EQ(ScreamingSnakeToUpperCamelCase("camel_case"), "CamelCase"); + EXPECT_EQ(ScreamingSnakeToUpperCamelCase("CAMEL_CASE_TRIO"), "CamelCaseTrio"); + EXPECT_EQ(ScreamingSnakeToUpperCamelCase("UNDER_IN__MIDDLE"), + "UnderInMiddle"); +} + } // namespace diff --git a/src/google/protobuf/compiler/rust/oneof.cc b/src/google/protobuf/compiler/rust/oneof.cc index 4b08f4571c..1408bfcd94 100644 --- a/src/google/protobuf/compiler/rust/oneof.cc +++ b/src/google/protobuf/compiler/rust/oneof.cc @@ -74,24 +74,6 @@ namespace rust { // } namespace { -std::string ToCamelCase(absl::string_view name) { - return cpp::UnderscoresToCamelCase(name, /* upper initial letter */ true); -} - -std::string OneofViewEnumRsName(const OneofDescriptor& oneof) { - return ToCamelCase(oneof.name()); -} - -std::string OneofMutEnumRsName(const OneofDescriptor& oneof) { - return ToCamelCase(oneof.name()) + "Mut"; -} - -std::string OneofCaseEnumName(const OneofDescriptor& oneof) { - // Note: This is the name used for the cpp Case enum, we use it for both - // the Rust Case enum as well as for the cpp case enum in the cpp thunk. - return ToCamelCase(oneof.name()) + "Case"; -} - std::string RsTypeNameView(Context& ctx, const FieldDescriptor& field) { if (field.options().has_ctype()) { return ""; // TODO: b/308792377 - ctype fields not supported yet. @@ -177,7 +159,7 @@ void GenerateOneofDefinition(Context& ctx, const OneofDescriptor& oneof) { if (rs_type.empty()) { continue; } - ctx.Emit({{"name", ToCamelCase(field.name())}, + ctx.Emit({{"name", OneofCaseRsName(field)}, {"type", rs_type}, {"number", std::to_string(field.number())}}, R"rs($name$($type$) = $number$, @@ -192,7 +174,7 @@ void GenerateOneofDefinition(Context& ctx, const OneofDescriptor& oneof) { if (rs_type.empty()) { continue; } - ctx.Emit({{"name", ToCamelCase(field.name())}, + ctx.Emit({{"name", OneofCaseRsName(field)}, {"type", rs_type}, {"number", std::to_string(field.number())}}, R"rs($name$($type$) = $number$, @@ -230,12 +212,12 @@ void GenerateOneofDefinition(Context& ctx, const OneofDescriptor& oneof) { // Note: This enum is used as the Thunk return type for getting which case is // used: it exactly matches the generate case enum that both cpp and upb use. - ctx.Emit({{"case_enum_name", OneofCaseEnumName(oneof)}, + ctx.Emit({{"case_enum_name", OneofCaseEnumRsName(oneof)}, {"cases", [&] { for (int i = 0; i < oneof.field_count(); ++i) { auto& field = *oneof.field(i); - ctx.Emit({{"name", ToCamelCase(field.name())}, + ctx.Emit({{"name", OneofCaseRsName(field)}, {"number", std::to_string(field.number())}}, R"rs($name$ = $number$, )rs"); @@ -260,7 +242,7 @@ void GenerateOneofAccessors(Context& ctx, const OneofDescriptor& oneof) { {{"oneof_name", oneof.name()}, {"view_enum_name", OneofViewEnumRsName(oneof)}, {"mut_enum_name", OneofMutEnumRsName(oneof)}, - {"case_enum_name", OneofCaseEnumName(oneof)}, + {"case_enum_name", OneofCaseEnumRsName(oneof)}, {"view_cases", [&] { for (int i = 0; i < oneof.field_count(); ++i) { @@ -271,7 +253,7 @@ void GenerateOneofAccessors(Context& ctx, const OneofDescriptor& oneof) { } ctx.Emit( { - {"case", ToCamelCase(field.name())}, + {"case", OneofCaseRsName(field)}, {"rs_getter", field.name()}, {"type", rs_type}, }, @@ -290,7 +272,7 @@ void GenerateOneofAccessors(Context& ctx, const OneofDescriptor& oneof) { continue; } ctx.Emit( - {{"case", ToCamelCase(field.name())}, + {{"case", OneofCaseRsName(field)}, {"rs_mut_getter", field.name() + "_mut"}, {"type", rs_type}, @@ -341,7 +323,7 @@ void GenerateOneofAccessors(Context& ctx, const OneofDescriptor& oneof) { void GenerateOneofExternC(Context& ctx, const OneofDescriptor& oneof) { ctx.Emit( { - {"case_enum_rs_name", OneofCaseEnumName(oneof)}, + {"case_enum_rs_name", OneofCaseEnumRsName(oneof)}, {"case_thunk", ThunkName(ctx, oneof, "case")}, }, R"rs( @@ -353,7 +335,7 @@ void GenerateOneofThunkCc(Context& ctx, const OneofDescriptor& oneof) { ctx.Emit( { {"oneof_name", oneof.name()}, - {"case_enum_name", OneofCaseEnumName(oneof)}, + {"case_enum_name", OneofCaseEnumRsName(oneof)}, {"case_thunk", ThunkName(ctx, oneof, "case")}, {"QualifiedMsg", cpp::QualifiedClassName(oneof.containing_type())}, },