Refactor oneof/enum naming functions for consistency

PiperOrigin-RevId: 596734937
pull/15319/head
Alyssa Haroldsen 11 months ago committed by Copybara-Service
parent f8d7885236
commit c16e0485c2
  1. 3
      src/google/protobuf/compiler/rust/BUILD.bazel
  2. 47
      src/google/protobuf/compiler/rust/enum.cc
  3. 8
      src/google/protobuf/compiler/rust/enum.h
  4. 35
      src/google/protobuf/compiler/rust/enum_test.cc
  5. 67
      src/google/protobuf/compiler/rust/naming.cc
  6. 18
      src/google/protobuf/compiler/rust/naming.h
  7. 35
      src/google/protobuf/compiler/rust/naming_test.cc
  8. 36
      src/google/protobuf/compiler/rust/oneof.cc

@ -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",
],

@ -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<std::pair<absl::string_view, int32_t>> EnumValuesInput(
const EnumDescriptor& desc) {
@ -126,46 +121,8 @@ std::vector<RustEnumValue> 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<RustEnumValue> values =
EnumValues(desc.name(), EnumValuesInput(desc));

@ -40,14 +40,6 @@ std::vector<RustEnumValue> EnumValues(
absl::string_view enum_name,
absl::Span<const std::pair<absl::string_view, int32_t>> 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

@ -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 <class Aliases>
auto EnumValue(absl::string_view name, int32_t number, Aliases aliases) {
return AllOf(Field("name", &RustEnumValue::name, Eq(name)),

@ -11,6 +11,7 @@
#include <vector>
#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

@ -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

@ -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

@ -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())},
},

Loading…
Cancel
Save