Allow Messages to be used in Oneofs.
PiperOrigin-RevId: 583347013
diff --git a/rust/test/shared/accessors_proto3_test.rs b/rust/test/shared/accessors_proto3_test.rs
index 3378ba6..945af2a 100644
--- a/rust/test/shared/accessors_proto3_test.rs
+++ b/rust/test/shared/accessors_proto3_test.rs
@@ -199,11 +199,23 @@
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]
diff --git a/src/google/protobuf/compiler/rust/oneof.cc b/src/google/protobuf/compiler/rust/oneof.cc
index 7f2671d..8335028 100644
--- a/src/google/protobuf/compiler/rust/oneof.cc
+++ b/src/google/protobuf/compiler/rust/oneof.cc
@@ -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 @@
}
// 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 @@
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 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 @@
{"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 @@
{"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 @@
{"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},
- },
+ {{"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.
+ // 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")}},