Allow Messages to be used in Oneofs.

PiperOrigin-RevId: 583347013
pull/14787/head
Protobuf Team Bot 1 year ago committed by Copybara-Service
parent b0d7441f83
commit 4864e43b7d
  1. 14
      rust/test/shared/accessors_proto3_test.rs
  2. 92
      src/google/protobuf/compiler/rust/oneof.cc

@ -199,11 +199,23 @@ fn test_oneof_accessors() {
assert_that!(msg.oneof_uint32_opt(), eq(Optional::Unset(0)));
assert_that!(msg.oneof_field(), matches_pattern!(not_set(_)));
// TODO: the submessage api is still in progress so we can't yet
// cause a submsg to be set here.
// msg.oneof_nested_message_mut().or_default(); // Cause the nested_message
// field to become set.
// assert_that!(msg.oneof_bytes_opt(),
// eq(Optional::Unset(_))); assert_that!(msg.oneof_field(),
// matches_pattern!(OneofNestedMessage(_)));
msg.oneof_uint32_set(Some(7));
msg.oneof_bytes_mut().set(b"123");
assert_that!(msg.oneof_uint32_opt(), eq(Optional::Unset(0)));
assert_that!(msg.oneof_field(), matches_pattern!(OneofBytes(eq(b"123"))));
msg.oneof_bytes_mut().clear();
assert_that!(msg.oneof_field(), matches_pattern!(not_set(_)));
}
#[test]

@ -9,6 +9,7 @@
#include <string>
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "google/protobuf/compiler/cpp/helpers.h"
#include "google/protobuf/compiler/rust/context.h"
@ -91,7 +92,9 @@ std::string oneofCaseEnumName(const OneofDescriptor& desc) {
}
// TODO: Promote up to naming.h once all types can be spelled.
std::string RsTypeName(const FieldDescriptor& desc) {
std::string RsTypeName(Context<FieldDescriptor> field) {
const auto& desc = field.desc();
// TODO: Fields with a ctype set not supported in v0.6 api.
if (desc.options().has_ctype()) {
return "";
@ -99,6 +102,8 @@ std::string RsTypeName(const FieldDescriptor& desc) {
switch (desc.type()) {
case FieldDescriptor::TYPE_MESSAGE:
return absl::StrCat("crate::", GetCrateRelativeQualifiedPath(
field.WithDesc(desc.message_type())));
case FieldDescriptor::TYPE_ENUM:
case FieldDescriptor::TYPE_GROUP:
return "";
@ -107,16 +112,16 @@ std::string RsTypeName(const FieldDescriptor& desc) {
}
}
std::string RsTypeNameView(const FieldDescriptor& desc) {
std::string type = RsTypeName(desc);
std::string RsTypeNameView(Context<FieldDescriptor> field) {
std::string type = RsTypeName(field);
if (type.empty()) return "";
return "View<'msg, " + type + ">";
return absl::StrCat("::__pb::View<'msg, ", type, ">");
}
std::string RsTypeNameMut(const FieldDescriptor& desc) {
std::string type = RsTypeName(desc);
std::string RsTypeNameMut(Context<FieldDescriptor> field) {
std::string type = RsTypeName(field);
if (type.empty()) return "";
return "Mut<'msg, " + type + ">";
return absl::StrCat("::__pb::Mut<'msg, ", type, ">");
}
} // namespace
@ -130,30 +135,30 @@ void GenerateOneofDefinition(Context<OneofDescriptor> oneof) {
{"view_fields",
[&] {
for (int i = 0; i < desc.field_count(); ++i) {
const auto& field = desc.field(i);
std::string rs_type = RsTypeNameView(*field);
const auto& field = *desc.field(i);
std::string rs_type = RsTypeNameView(oneof.WithDesc(field));
if (rs_type.empty()) {
continue;
}
oneof.Emit({{"name", ToCamelCase(field->name())},
oneof.Emit({{"name", ToCamelCase(field.name())},
{"type", rs_type},
{"number", std::to_string(field->number())}},
R"rs($name$($pb$::$type$) = $number$,
{"number", std::to_string(field.number())}},
R"rs($name$($type$) = $number$,
)rs");
}
}},
{"mut_fields",
[&] {
for (int i = 0; i < desc.field_count(); ++i) {
const auto& field = desc.field(i);
std::string rs_type = RsTypeNameMut(*field);
const auto& field = *desc.field(i);
std::string rs_type = RsTypeNameMut(oneof.WithDesc(field));
if (rs_type.empty()) {
continue;
}
oneof.Emit({{"name", ToCamelCase(field->name())},
oneof.Emit({{"name", ToCamelCase(field.name())},
{"type", rs_type},
{"number", std::to_string(field->number())}},
R"rs($name$($pb$::$type$) = $number$,
{"number", std::to_string(field.number())}},
R"rs($name$($type$) = $number$,
)rs");
}
}}},
@ -223,15 +228,15 @@ void GenerateOneofAccessors(Context<OneofDescriptor> oneof) {
{"view_cases",
[&] {
for (int i = 0; i < desc.field_count(); ++i) {
const auto& field = desc.field(i);
std::string rs_type = RsTypeNameView(*field);
const auto& field = *desc.field(i);
std::string rs_type = RsTypeNameView(oneof.WithDesc(field));
if (rs_type.empty()) {
continue;
}
oneof.Emit(
{
{"case", ToCamelCase(field->name())},
{"rs_getter", field->name()},
{"case", ToCamelCase(field.name())},
{"rs_getter", field.name()},
{"type", rs_type},
},
R"rs($Msg$_::$case_enum_name$::$case$ => $Msg$_::$view_enum_name$::$case$(self.$rs_getter$()),
@ -241,31 +246,36 @@ void GenerateOneofAccessors(Context<OneofDescriptor> oneof) {
{"mut_cases",
[&] {
for (int i = 0; i < desc.field_count(); ++i) {
const auto& field = desc.field(i);
std::string rs_type = RsTypeNameMut(*field);
const auto& field = *desc.field(i);
std::string rs_type = RsTypeNameMut(oneof.WithDesc(field));
if (rs_type.empty()) {
continue;
}
oneof.Emit(
{
{"case", ToCamelCase(field->name())},
{"rs_mut_getter", field->name() + "_mut"},
{"type", rs_type},
},
// The flow here is:
// 1) First find out which oneof field is already set (if any)
// 2) If a field is set, call the corresponding field's _mut()
// and wrap that Mut<> in the SomeOneofMut eum.
// During step 2 this code uses try_into_mut().unwrap() instead
// of .or_default() so that it will panic if step 1 says that
// the field is set, but then the _mut() accessor for the
// corresponding field shows as unset; if that happened it would
// imply a severe error in protobuf code; .or_default() would
// silently continue and cause the field to become set on the
// message, which is not the intended behavior.
{{"case", ToCamelCase(field.name())},
{"rs_mut_getter", field.name() + "_mut"},
{"type", rs_type},
// Any extra behavior needed to map the mut getter into the
// unwrapped Mut<>. Right now Message's _mut already returns
// the Mut directly, but for scalars the accessor will return
// an Optional which we then grab the mut by doing
// .try_into_mut().unwrap().
//
// Note that this unwrap() is safe because the flow is:
// 1) Find out which oneof field is already set (if any)
// 2) If a field is set, call the corresponding field's _mut()
// and wrap the result in the SomeOneofMut enum.
// The unwrap() will only ever panic if the which oneof enum
// disagrees with the corresponding field presence which.
// TODO: If the message _mut accessor returns
// Optional<> then this conditional behavior should be removed.
{"into_mut_transform",
field.type() == FieldDescriptor::TYPE_MESSAGE
? ""
: ".try_into_mut().unwrap()"}},
R"rs($Msg$_::$case_enum_name$::$case$ =>
$Msg$_::$mut_enum_name$::$case$(self.$rs_mut_getter$().try_into_mut().unwrap()), )rs");
$Msg$_::$mut_enum_name$::$case$(self.$rs_mut_getter$()$into_mut_transform$), )rs");
}
}},
{"case_thunk", Thunk(oneof, "case")}},

Loading…
Cancel
Save