Initial skeleton towards oneof's Mut accessor, and clarify oneof the View accessor to use View<> instead of the raw primitive type.

PiperOrigin-RevId: 565156563
pull/14073/head
Protobuf Team Bot 2 years ago committed by Copybara-Service
parent 00c3369fb5
commit 09d2511a6c
  1. 10
      rust/test/shared/accessors_proto3_test.rs
  2. 10
      rust/test/shared/accessors_test.rs
  3. 113
      src/google/protobuf/compiler/rust/oneof.cc

@ -184,21 +184,23 @@ fn test_optional_string_accessors() {
#[test] #[test]
fn test_oneof_accessors() { fn test_oneof_accessors() {
use TestAllTypes_::OneofField::*;
let mut msg = TestAllTypes::new(); let mut msg = TestAllTypes::new();
assert_eq!(msg.oneof_field(), TestAllTypes_::OneofField::not_set); assert!(matches!(msg.oneof_field(), not_set(_)));
msg.oneof_uint32_set(Some(7)); msg.oneof_uint32_set(Some(7));
assert_eq!(msg.oneof_uint32_opt(), Optional::Set(7)); assert_eq!(msg.oneof_uint32_opt(), Optional::Set(7));
assert_eq!(msg.oneof_field(), TestAllTypes_::OneofField::OneofUint32(7)); assert!(matches!(msg.oneof_field(), OneofUint32(7)));
msg.oneof_uint32_set(None); msg.oneof_uint32_set(None);
assert_eq!(msg.oneof_uint32_opt(), Optional::Unset(0)); assert_eq!(msg.oneof_uint32_opt(), Optional::Unset(0));
assert_eq!(msg.oneof_field(), TestAllTypes_::OneofField::not_set); assert!(matches!(msg.oneof_field(), not_set(_)));
msg.oneof_uint32_set(Some(7)); msg.oneof_uint32_set(Some(7));
msg.oneof_bytes_mut().set(b""); msg.oneof_bytes_mut().set(b"");
assert_eq!(msg.oneof_uint32_opt(), Optional::Unset(0)); assert_eq!(msg.oneof_uint32_opt(), Optional::Unset(0));
// This should show it set to the OneofBytes but its not supported yet. // This should show it set to the OneofBytes but its not supported yet.
assert_eq!(msg.oneof_field(), TestAllTypes_::OneofField::not_set); assert!(matches!(msg.oneof_field(), not_set(_)));
} }

@ -373,20 +373,22 @@ fn test_singular_msg_field() {
#[test] #[test]
fn test_oneof_accessors() { fn test_oneof_accessors() {
use TestAllTypes_::OneofField::*;
let mut msg = TestAllTypes::new(); let mut msg = TestAllTypes::new();
assert_eq!(msg.oneof_field(), TestAllTypes_::OneofField::not_set); assert!(matches!(msg.oneof_field(), not_set(_)));
msg.oneof_uint32_set(Some(7)); msg.oneof_uint32_set(Some(7));
assert_eq!(msg.oneof_uint32_opt(), Optional::Set(7)); assert_eq!(msg.oneof_uint32_opt(), Optional::Set(7));
assert_eq!(msg.oneof_field(), TestAllTypes_::OneofField::OneofUint32(7)); assert!(matches!(msg.oneof_field(), OneofUint32(7)));
msg.oneof_uint32_set(None); msg.oneof_uint32_set(None);
assert_eq!(msg.oneof_uint32_opt(), Optional::Unset(0)); assert_eq!(msg.oneof_uint32_opt(), Optional::Unset(0));
assert_eq!(msg.oneof_field(), TestAllTypes_::OneofField::not_set); assert!(matches!(msg.oneof_field(), not_set(_)));
msg.oneof_uint32_set(Some(7)); msg.oneof_uint32_set(Some(7));
msg.oneof_bytes_mut().set(b""); msg.oneof_bytes_mut().set(b"");
assert_eq!(msg.oneof_uint32_opt(), Optional::Unset(0)); assert_eq!(msg.oneof_uint32_opt(), Optional::Unset(0));
// This should show it set to the OneofBytes but its not supported yet. // This should show it set to the OneofBytes but its not supported yet.
assert_eq!(msg.oneof_field(), TestAllTypes_::OneofField::not_set); assert!(matches!(msg.oneof_field(), not_set(_)));
} }

@ -44,20 +44,26 @@ namespace rust {
// This will emit as the exposed API: // This will emit as the exposed API:
// pub mod SomeMsg_ { // pub mod SomeMsg_ {
// // The 'view' struct (no suffix on the name) // // The 'view' struct (no suffix on the name)
// pub enum SomeOneof { // pub enum SomeOneof<'msg> {
// FieldA(i32) = 7, // FieldA(View<'msg, i32>) = 7,
// FieldB(u32) = 9, // FieldB(View<'msg, u32>) = 9,
// not_set = 0
// }
// pub enum SomeOneofMut<'msg> {
// FieldA(Mut<'msg, i32>) = 7,
// FieldB(Mut<'msg, u32>) = 9,
// not_set = 0 // not_set = 0
// } // }
// } // }
// impl SomeMsg { // impl SomeMsg {
// pub fn some_oneof() -> SomeOneof {...} // pub fn some_oneof() -> SomeOneof {...}
// pub fn some_oneof_mut() -> SomeOneofMut {...}
// } // }
// //
// An additional "Case" enum which just reflects the corresponding slot numbers // An additional "Case" enum which just reflects the corresponding slot numbers
// is emitted for usage with the FFI (exactly matching the Case struct that both // is emitted for usage with the FFI (exactly matching the Case struct that both
// cpp and upb generate). This enum is pub(super) which makes it private to the // cpp and upb generate).
// codegen since its inside an inner mod:
// //
// #[repr(C)] pub(super) enum SomeOneofCase { // #[repr(C)] pub(super) enum SomeOneofCase {
// FieldA = 7, // FieldA = 7,
@ -74,10 +80,14 @@ std::string oneofViewEnumRsName(const OneofDescriptor& desc) {
return ToCamelCase(desc.name()); return ToCamelCase(desc.name());
} }
std::string oneofMutEnumRsName(const OneofDescriptor& desc) {
return ToCamelCase(desc.name()) + "Mut";
}
std::string oneofCaseEnumName(const OneofDescriptor& desc) { std::string oneofCaseEnumName(const OneofDescriptor& desc) {
// Note: This is the name used for the cpp Case enum, we use it for both // 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. // the Rust Case enum as well as for the cpp case enum in the cpp thunk.
return oneofViewEnumRsName(desc) + "Case"; return ToCamelCase(desc.name()) + "Case";
} }
// TODO(b/285309334): Promote up to naming.h once all types can be spelled. // TODO(b/285309334): Promote up to naming.h once all types can be spelled.
@ -94,6 +104,18 @@ std::string RsTypeName(const FieldDescriptor& desc) {
} }
} }
std::string RsTypeNameView(const FieldDescriptor& desc) {
std::string type = RsTypeName(desc);
if (type.empty()) return "";
return "View<'msg, " + type + ">";
}
std::string RsTypeNameMut(const FieldDescriptor& desc) {
std::string type = RsTypeName(desc);
if (type.empty()) return "";
return "Mut<'msg, " + type + ">";
}
} // namespace } // namespace
void GenerateOneofDefinition(Context<OneofDescriptor> oneof) { void GenerateOneofDefinition(Context<OneofDescriptor> oneof) {
@ -101,31 +123,62 @@ void GenerateOneofDefinition(Context<OneofDescriptor> oneof) {
oneof.Emit( oneof.Emit(
{{"view_enum_name", oneofViewEnumRsName(desc)}, {{"view_enum_name", oneofViewEnumRsName(desc)},
{"mut_enum_name", oneofMutEnumRsName(desc)},
{"view_fields", {"view_fields",
[&] { [&] {
for (int i = 0; i < desc.field_count(); ++i) { for (int i = 0; i < desc.field_count(); ++i) {
const auto& field = desc.field(i); const auto& field = desc.field(i);
std::string rs_type = RsTypeName(*field); std::string rs_type = RsTypeNameView(*field);
if (rs_type.empty()) {
continue;
}
oneof.Emit({{"name", ToCamelCase(field->name())},
{"type", rs_type},
{"number", std::to_string(field->number())}},
R"rs($name$($pb$::$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);
if (rs_type.empty()) { if (rs_type.empty()) {
continue; continue;
} }
oneof.Emit({{"name", ToCamelCase(field->name())}, oneof.Emit({{"name", ToCamelCase(field->name())},
{"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$($pb$::$type$) = $number$,
)rs"); )rs");
} }
}}}, }}},
// TODO(b/297342638): Revisit if isize is the optimal repr for this enum. // TODO(b/297342638): Revisit if isize is the optimal repr for this enum.
// TODO(b/285309334): not_set currently has phantom data just to avoid the
// lifetime on the enum breaking compilation if there are zero supported
// fields on it (e.g. if the oneof only has Messages inside).
R"rs( R"rs(
#[non_exhaustive] #[non_exhaustive]
#[derive(Debug, PartialEq)] #[derive(Debug)]
#[allow(dead_code)]
#[repr(isize)] #[repr(isize)]
pub enum $view_enum_name$ { pub enum $view_enum_name$<'msg> {
$view_fields$ $view_fields$
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
not_set = 0 not_set(std::marker::PhantomData<&'msg ()>) = 0
}
#[non_exhaustive]
#[derive(Debug)]
#[allow(dead_code)]
#[repr(isize)]
pub enum $mut_enum_name$<'msg> {
$mut_fields$
#[allow(non_camel_case_types)]
not_set(std::marker::PhantomData<&'msg ()>) = 0
} }
)rs"); )rs");
@ -145,7 +198,7 @@ void GenerateOneofDefinition(Context<OneofDescriptor> oneof) {
}}}, }}},
R"rs( R"rs(
#[repr(C)] #[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub(super) enum $case_enum_name$ { pub(super) enum $case_enum_name$ {
$cases$ $cases$
@ -162,12 +215,13 @@ void GenerateOneofAccessors(Context<OneofDescriptor> oneof) {
oneof.Emit( oneof.Emit(
{{"oneof_name", desc.name()}, {{"oneof_name", desc.name()},
{"view_enum_name", oneofViewEnumRsName(desc)}, {"view_enum_name", oneofViewEnumRsName(desc)},
{"mut_enum_name", oneofMutEnumRsName(desc)},
{"case_enum_name", oneofCaseEnumName(desc)}, {"case_enum_name", oneofCaseEnumName(desc)},
{"cases", {"view_cases",
[&] { [&] {
for (int i = 0; i < desc.field_count(); ++i) { for (int i = 0; i < desc.field_count(); ++i) {
const auto& field = desc.field(i); const auto& field = desc.field(i);
std::string rs_type = RsTypeName(*field); std::string rs_type = RsTypeNameView(*field);
if (rs_type.empty()) { if (rs_type.empty()) {
continue; continue;
} }
@ -181,12 +235,39 @@ void GenerateOneofAccessors(Context<OneofDescriptor> oneof) {
)rs"); )rs");
} }
}}, }},
{"mut_cases",
[&] {
for (int i = 0; i < desc.field_count(); ++i) {
const auto& field = desc.field(i);
std::string rs_type = RsTypeNameMut(*field);
if (rs_type.empty()) {
continue;
}
// TODO(b/285309334, 285309449): Uncomment this to allow mut once
// _mut() on singular fields with presence is implemented.
/*oneof.Emit({
{"case", ToCamelCase(field->name())},
{"rs_getter", field->name() + "_mut"},
{"type", rs_type},
},
R"rs($Msg$_::$case_enum_name$::$case$ =>
$Msg$_::$mut_enum_name$::$case$(self.$rs_getter$()), )rs");
*/
}
}},
{"case_thunk", Thunk(oneof, "case")}}, {"case_thunk", Thunk(oneof, "case")}},
R"rs( R"rs(
pub fn r#$oneof_name$(&self) -> $Msg$_::$view_enum_name$ { pub fn r#$oneof_name$(&self) -> $Msg$_::$view_enum_name$ {
match unsafe { $case_thunk$(self.inner.msg) } { match unsafe { $case_thunk$(self.inner.msg) } {
$cases$ $view_cases$
_ => $Msg$_::$view_enum_name$::not_set _ => $Msg$_::$view_enum_name$::not_set(std::marker::PhantomData)
}
}
pub fn r#$oneof_name$_mut(&mut self) -> $Msg$_::$mut_enum_name$ {
match unsafe { $case_thunk$(self.inner.msg) } {
$mut_cases$
_ => $Msg$_::$mut_enum_name$::not_set(std::marker::PhantomData)
} }
} }

Loading…
Cancel
Save