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", strip_include_prefix = "/src",
deps = [ deps = [
":context", ":context",
":naming",
"//src/google/protobuf", "//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_map",
"@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/log:absl_check", "@com_google_absl//absl/log:absl_check",
@ -151,6 +151,7 @@ cc_library(
":context", ":context",
"//src/google/protobuf", "//src/google/protobuf",
"//src/google/protobuf/compiler:code_generator", "//src/google/protobuf/compiler:code_generator",
"//src/google/protobuf/compiler/cpp:names_internal",
"@com_google_absl//absl/log:absl_log", "@com_google_absl//absl/log:absl_log",
"@com_google_absl//absl/strings", "@com_google_absl//absl/strings",
], ],

@ -24,8 +24,8 @@
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
#include "absl/strings/strip.h" #include "absl/strings/strip.h"
#include "absl/types/span.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/context.h"
#include "google/protobuf/compiler/rust/naming.h"
#include "google/protobuf/descriptor.h" #include "google/protobuf/descriptor.h"
namespace google { namespace google {
@ -34,11 +34,6 @@ namespace compiler {
namespace rust { namespace rust {
namespace { namespace {
std::string EnumName(const EnumDescriptor& desc) {
return cpp::UnderscoresToCamelCase(desc.name(), /*cap first letter=*/true);
}
// Constructs input for `EnumValues` from an enum descriptor. // Constructs input for `EnumValues` from an enum descriptor.
std::vector<std::pair<absl::string_view, int32_t>> EnumValuesInput( std::vector<std::pair<absl::string_view, int32_t>> EnumValuesInput(
const EnumDescriptor& desc) { const EnumDescriptor& desc) {
@ -126,46 +121,8 @@ std::vector<RustEnumValue> EnumValues(
return result; 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) { void GenerateEnumDefinition(Context& ctx, const EnumDescriptor& desc) {
std::string name = EnumName(desc); std::string name = EnumRsName(desc);
ABSL_CHECK(desc.value_count() > 0); ABSL_CHECK(desc.value_count() > 0);
std::vector<RustEnumValue> values = std::vector<RustEnumValue> values =
EnumValues(desc.name(), EnumValuesInput(desc)); EnumValues(desc.name(), EnumValuesInput(desc));

@ -40,14 +40,6 @@ std::vector<RustEnumValue> EnumValues(
absl::string_view enum_name, absl::string_view enum_name,
absl::Span<const std::pair<absl::string_view, int32_t>> values); 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 rust
} // namespace compiler } // namespace compiler
} // namespace protobuf } // namespace protobuf

@ -8,49 +8,14 @@
namespace { namespace {
using ::google::protobuf::compiler::rust::CamelToSnakeCase;
using ::google::protobuf::compiler::rust::EnumValues; using ::google::protobuf::compiler::rust::EnumValues;
using ::google::protobuf::compiler::rust::RustEnumValue; using ::google::protobuf::compiler::rust::RustEnumValue;
using ::google::protobuf::compiler::rust::ScreamingSnakeToUpperCamelCase;
using ::testing::AllOf; using ::testing::AllOf;
using ::testing::ElementsAre; using ::testing::ElementsAre;
using ::testing::Eq; using ::testing::Eq;
using ::testing::Field; using ::testing::Field;
using ::testing::IsEmpty; 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> template <class Aliases>
auto EnumValue(absl::string_view name, int32_t number, Aliases aliases) { auto EnumValue(absl::string_view name, int32_t number, Aliases aliases) {
return AllOf(Field("name", &RustEnumValue::name, Eq(name)), return AllOf(Field("name", &RustEnumValue::name, Eq(name)),

@ -11,6 +11,7 @@
#include <vector> #include <vector>
#include "absl/log/absl_log.h" #include "absl/log/absl_log.h"
#include "absl/strings/ascii.h"
#include "absl/strings/str_cat.h" #include "absl/strings/str_cat.h"
#include "absl/strings/str_join.h" #include "absl/strings/str_join.h"
#include "absl/strings/str_replace.h" #include "absl/strings/str_replace.h"
@ -18,6 +19,7 @@
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
#include "absl/strings/substitute.h" #include "absl/strings/substitute.h"
#include "google/protobuf/compiler/code_generator.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/compiler/rust/context.h"
#include "google/protobuf/descriptor.h" #include "google/protobuf/descriptor.h"
@ -238,6 +240,71 @@ std::string FieldInfoComment(Context& ctx, const FieldDescriptor& field) {
return comment; 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 rust
} // namespace compiler } // namespace compiler
} // namespace protobuf } // namespace protobuf

@ -35,6 +35,13 @@ std::string ThunkName(Context& ctx, const Descriptor& msg,
std::string PrimitiveRsTypeName(const FieldDescriptor& field); 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 FieldInfoComment(Context& ctx, const FieldDescriptor& field);
std::string RustModule(Context& ctx, const Descriptor& msg); 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); 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 rust
} // namespace compiler } // namespace compiler
} // namespace protobuf } // namespace protobuf

@ -8,11 +8,13 @@
#include "google/protobuf/descriptor.h" #include "google/protobuf/descriptor.h"
#include "google/protobuf/io/zero_copy_stream_impl_lite.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::Context;
using google::protobuf::compiler::rust::Kernel; using google::protobuf::compiler::rust::Kernel;
using google::protobuf::compiler::rust::Options; using google::protobuf::compiler::rust::Options;
using google::protobuf::compiler::rust::RustGeneratorContext; using google::protobuf::compiler::rust::RustGeneratorContext;
using google::protobuf::compiler::rust::RustInternalModuleName; using google::protobuf::compiler::rust::RustInternalModuleName;
using google::protobuf::compiler::rust::ScreamingSnakeToUpperCamelCase;
using google::protobuf::io::Printer; using google::protobuf::io::Printer;
using google::protobuf::io::StringOutputStream; using google::protobuf::io::StringOutputStream;
@ -34,4 +36,37 @@ TEST(RustProtoNaming, RustInternalModuleName) {
EXPECT_EQ(RustInternalModuleName(c, *fd), "strong__bad_slol"); 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 } // namespace

@ -74,24 +74,6 @@ namespace rust {
// } // }
namespace { 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) { std::string RsTypeNameView(Context& ctx, const FieldDescriptor& field) {
if (field.options().has_ctype()) { if (field.options().has_ctype()) {
return ""; // TODO: b/308792377 - ctype fields not supported yet. return ""; // TODO: b/308792377 - ctype fields not supported yet.
@ -177,7 +159,7 @@ void GenerateOneofDefinition(Context& ctx, const OneofDescriptor& oneof) {
if (rs_type.empty()) { if (rs_type.empty()) {
continue; continue;
} }
ctx.Emit({{"name", ToCamelCase(field.name())}, ctx.Emit({{"name", OneofCaseRsName(field)},
{"type", rs_type}, {"type", rs_type},
{"number", std::to_string(field.number())}}, {"number", std::to_string(field.number())}},
R"rs($name$($type$) = $number$, R"rs($name$($type$) = $number$,
@ -192,7 +174,7 @@ void GenerateOneofDefinition(Context& ctx, const OneofDescriptor& oneof) {
if (rs_type.empty()) { if (rs_type.empty()) {
continue; continue;
} }
ctx.Emit({{"name", ToCamelCase(field.name())}, ctx.Emit({{"name", OneofCaseRsName(field)},
{"type", rs_type}, {"type", rs_type},
{"number", std::to_string(field.number())}}, {"number", std::to_string(field.number())}},
R"rs($name$($type$) = $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 // 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. // 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", {"cases",
[&] { [&] {
for (int i = 0; i < oneof.field_count(); ++i) { for (int i = 0; i < oneof.field_count(); ++i) {
auto& field = *oneof.field(i); auto& field = *oneof.field(i);
ctx.Emit({{"name", ToCamelCase(field.name())}, ctx.Emit({{"name", OneofCaseRsName(field)},
{"number", std::to_string(field.number())}}, {"number", std::to_string(field.number())}},
R"rs($name$ = $number$, R"rs($name$ = $number$,
)rs"); )rs");
@ -260,7 +242,7 @@ void GenerateOneofAccessors(Context& ctx, const OneofDescriptor& oneof) {
{{"oneof_name", oneof.name()}, {{"oneof_name", oneof.name()},
{"view_enum_name", OneofViewEnumRsName(oneof)}, {"view_enum_name", OneofViewEnumRsName(oneof)},
{"mut_enum_name", OneofMutEnumRsName(oneof)}, {"mut_enum_name", OneofMutEnumRsName(oneof)},
{"case_enum_name", OneofCaseEnumName(oneof)}, {"case_enum_name", OneofCaseEnumRsName(oneof)},
{"view_cases", {"view_cases",
[&] { [&] {
for (int i = 0; i < oneof.field_count(); ++i) { for (int i = 0; i < oneof.field_count(); ++i) {
@ -271,7 +253,7 @@ void GenerateOneofAccessors(Context& ctx, const OneofDescriptor& oneof) {
} }
ctx.Emit( ctx.Emit(
{ {
{"case", ToCamelCase(field.name())}, {"case", OneofCaseRsName(field)},
{"rs_getter", field.name()}, {"rs_getter", field.name()},
{"type", rs_type}, {"type", rs_type},
}, },
@ -290,7 +272,7 @@ void GenerateOneofAccessors(Context& ctx, const OneofDescriptor& oneof) {
continue; continue;
} }
ctx.Emit( ctx.Emit(
{{"case", ToCamelCase(field.name())}, {{"case", OneofCaseRsName(field)},
{"rs_mut_getter", field.name() + "_mut"}, {"rs_mut_getter", field.name() + "_mut"},
{"type", rs_type}, {"type", rs_type},
@ -341,7 +323,7 @@ void GenerateOneofAccessors(Context& ctx, const OneofDescriptor& oneof) {
void GenerateOneofExternC(Context& ctx, const OneofDescriptor& oneof) { void GenerateOneofExternC(Context& ctx, const OneofDescriptor& oneof) {
ctx.Emit( ctx.Emit(
{ {
{"case_enum_rs_name", OneofCaseEnumName(oneof)}, {"case_enum_rs_name", OneofCaseEnumRsName(oneof)},
{"case_thunk", ThunkName(ctx, oneof, "case")}, {"case_thunk", ThunkName(ctx, oneof, "case")},
}, },
R"rs( R"rs(
@ -353,7 +335,7 @@ void GenerateOneofThunkCc(Context& ctx, const OneofDescriptor& oneof) {
ctx.Emit( ctx.Emit(
{ {
{"oneof_name", oneof.name()}, {"oneof_name", oneof.name()},
{"case_enum_name", OneofCaseEnumName(oneof)}, {"case_enum_name", OneofCaseEnumRsName(oneof)},
{"case_thunk", ThunkName(ctx, oneof, "case")}, {"case_thunk", ThunkName(ctx, oneof, "case")},
{"QualifiedMsg", cpp::QualifiedClassName(oneof.containing_type())}, {"QualifiedMsg", cpp::QualifiedClassName(oneof.containing_type())},
}, },

Loading…
Cancel
Save