From 99d4060ec26de4a01a01c3244090a315e9dbba09 Mon Sep 17 00:00:00 2001 From: Protobuf Team Bot Date: Fri, 25 Aug 2023 06:08:31 -0700 Subject: [PATCH] Implement initial oneof support in Rust pb. PiperOrigin-RevId: 560070076 --- rust/test/shared/accessors_proto3_test.rs | 23 +- rust/test/shared/accessors_test.rs | 22 +- src/google/protobuf/compiler/rust/BUILD.bazel | 20 +- .../rust/accessors/singular_scalar.cc | 4 +- src/google/protobuf/compiler/rust/message.cc | 89 +++++-- src/google/protobuf/compiler/rust/naming.cc | 35 ++- src/google/protobuf/compiler/rust/naming.h | 5 +- src/google/protobuf/compiler/rust/oneof.cc | 250 ++++++++++++++++++ src/google/protobuf/compiler/rust/oneof.h | 52 ++++ 9 files changed, 460 insertions(+), 40 deletions(-) create mode 100644 src/google/protobuf/compiler/rust/oneof.cc create mode 100644 src/google/protobuf/compiler/rust/oneof.h diff --git a/rust/test/shared/accessors_proto3_test.rs b/rust/test/shared/accessors_proto3_test.rs index 2789f11aef..4e8f55eb7d 100644 --- a/rust/test/shared/accessors_proto3_test.rs +++ b/rust/test/shared/accessors_proto3_test.rs @@ -31,7 +31,7 @@ /// Tests covering accessors for singular bool, int32, int64, and bytes fields /// on proto3. use protobuf::Optional; -use unittest_proto3::proto3_unittest::TestAllTypes; +use unittest_proto3::proto3_unittest::{TestAllTypes, TestAllTypes_}; use unittest_proto3_optional::proto2_unittest::TestProto3Optional; #[test] @@ -119,3 +119,24 @@ fn test_optional_bytes_accessors() { assert_eq!(msg.optional_bytes_mut().get(), b"\xffbinary\x85non-utf8"); assert_eq!(msg.optional_bytes_mut().or_default().get(), b"\xffbinary\x85non-utf8"); } + +#[test] +fn test_oneof_accessors() { + let mut msg = TestAllTypes::new(); + assert_eq!(msg.oneof_field(), TestAllTypes_::OneofField::not_set); + + msg.oneof_uint32_set(Some(7)); + assert_eq!(msg.oneof_uint32_opt(), Some(7)); + assert_eq!(msg.oneof_field(), TestAllTypes_::OneofField::OneofUint32(7)); + + msg.oneof_uint32_set(None); + assert_eq!(msg.oneof_uint32_opt(), None); + assert_eq!(msg.oneof_field(), TestAllTypes_::OneofField::not_set); + + msg.oneof_uint32_set(Some(7)); + msg.oneof_bytes_mut().set(b""); + assert_eq!(msg.oneof_uint32_opt(), None); + + // This should show it set to the OneofBytes but its not supported yet. + assert_eq!(msg.oneof_field(), TestAllTypes_::OneofField::not_set); +} diff --git a/rust/test/shared/accessors_test.rs b/rust/test/shared/accessors_test.rs index 32679ea808..4bedb53bc9 100644 --- a/rust/test/shared/accessors_test.rs +++ b/rust/test/shared/accessors_test.rs @@ -31,7 +31,7 @@ //! Tests covering accessors for singular bool, int32, int64, and bytes fields. use protobuf::Optional; -use unittest_proto::proto2_unittest::TestAllTypes; +use unittest_proto::proto2_unittest::{TestAllTypes, TestAllTypes_}; #[test] fn test_default_accessors() { @@ -314,3 +314,23 @@ fn test_singular_msg_field() { // call should look like msg.optional_nested_message().bb() let _msg: unittest_proto::proto2_unittest::TestAllTypesView = msg.optional_nested_message(); } + +#[test] +fn test_oneof_accessors() { + let mut msg = TestAllTypes::new(); + assert_eq!(msg.oneof_field(), TestAllTypes_::OneofField::not_set); + + msg.oneof_uint32_set(Some(7)); + assert_eq!(msg.oneof_uint32_opt(), Some(7)); + assert_eq!(msg.oneof_field(), TestAllTypes_::OneofField::OneofUint32(7)); + + msg.oneof_uint32_set(None); + assert_eq!(msg.oneof_uint32_opt(), None); + assert_eq!(msg.oneof_field(), TestAllTypes_::OneofField::not_set); + + msg.oneof_uint32_set(Some(7)); + msg.oneof_bytes_mut().set(b""); + assert_eq!(msg.oneof_uint32_opt(), None); + // This should show it set to the OneofBytes but its not supported yet. + assert_eq!(msg.oneof_field(), TestAllTypes_::OneofField::not_set); +} diff --git a/src/google/protobuf/compiler/rust/BUILD.bazel b/src/google/protobuf/compiler/rust/BUILD.bazel index 26008b43db..cda89d786a 100644 --- a/src/google/protobuf/compiler/rust/BUILD.bazel +++ b/src/google/protobuf/compiler/rust/BUILD.bazel @@ -39,6 +39,7 @@ cc_library( ":accessors", ":context", ":naming", + ":oneof", "//src/google/protobuf:protobuf_nowkt", "//src/google/protobuf/compiler/cpp:names", "@com_google_absl//absl/log:absl_check", @@ -56,8 +57,8 @@ cc_library( "accessors/unsupported_field.cc", ], hdrs = [ - "accessors/accessors.h", - "accessors/accessor_generator.h", + "accessors/accessor_generator.h", + "accessors/accessors.h", ], copts = COPTS, include_prefix = "google/protobuf/compiler/rust", @@ -103,6 +104,21 @@ cc_library( ], ) +cc_library( + name = "oneof", + srcs = ["oneof.cc"], + hdrs = ["oneof.h"], + copts = COPTS, + include_prefix = "google/protobuf/compiler/rust", + deps = [ + ":context", + ":naming", + "//src/google/protobuf:protobuf_nowkt", + "//src/google/protobuf/compiler/cpp:names", + "@com_google_absl//absl/strings", + ], +) + cc_library( name = "relative_path", srcs = ["relative_path.cc"], diff --git a/src/google/protobuf/compiler/rust/accessors/singular_scalar.cc b/src/google/protobuf/compiler/rust/accessors/singular_scalar.cc index 93a5d41b67..ebb94a6b58 100644 --- a/src/google/protobuf/compiler/rust/accessors/singular_scalar.cc +++ b/src/google/protobuf/compiler/rust/accessors/singular_scalar.cc @@ -44,7 +44,7 @@ void SingularScalar::InMsgImpl(Context field) const { field.Emit( { {"field", field.desc().name()}, - {"Scalar", PrimitiveRsTypeName(field)}, + {"Scalar", PrimitiveRsTypeName(field.desc())}, {"hazzer_thunk", Thunk(field, "has")}, {"getter", [&] { @@ -86,7 +86,7 @@ void SingularScalar::InMsgImpl(Context field) const { void SingularScalar::InExternC(Context field) const { field.Emit( - {{"Scalar", PrimitiveRsTypeName(field)}, + {{"Scalar", PrimitiveRsTypeName(field.desc())}, {"hazzer_thunk", Thunk(field, "has")}, {"getter_thunk", Thunk(field, "get")}, {"setter_thunk", Thunk(field, "set")}, diff --git a/src/google/protobuf/compiler/rust/message.cc b/src/google/protobuf/compiler/rust/message.cc index 6f9ce5a43e..1e2a15bf49 100644 --- a/src/google/protobuf/compiler/rust/message.cc +++ b/src/google/protobuf/compiler/rust/message.cc @@ -38,6 +38,7 @@ #include "google/protobuf/compiler/rust/accessors/accessors.h" #include "google/protobuf/compiler/rust/context.h" #include "google/protobuf/compiler/rust/naming.h" +#include "google/protobuf/compiler/rust/oneof.h" #include "google/protobuf/descriptor.h" namespace google { @@ -206,11 +207,18 @@ void GenerateRs(Context msg) { msg.Emit({{"comment", FieldInfoComment(field)}}, R"rs( // $comment$ )rs"); - GenerateAccessorMsgImpl(field); msg.printer().PrintRaw("\n"); } }}, + {"oneof_accessor_fns", + [&] { + for (int i = 0; i < msg.desc().real_oneof_decl_count(); ++i) { + GenerateOneofAccessors( + msg.WithDesc(*msg.desc().real_oneof_decl(i))); + msg.printer().PrintRaw("\n"); + } + }}, {"accessor_externs", [&] { for (int i = 0; i < msg.desc().field_count(); ++i) { @@ -218,9 +226,20 @@ void GenerateRs(Context msg) { msg.printer().PrintRaw("\n"); } }}, + {"oneof_externs", + [&] { + for (int i = 0; i < msg.desc().real_oneof_decl_count(); ++i) { + GenerateOneofExternC( + msg.WithDesc(*msg.desc().real_oneof_decl(i))); + msg.printer().PrintRaw("\n"); + } + }}, {"nested_msgs", [&] { - if (msg.desc().nested_type_count() == 0) { + // If we have no nested types or oneofs, bail out without emitting + // an empty mod SomeMsg_. + if (msg.desc().nested_type_count() == 0 && + msg.desc().real_oneof_decl_count() == 0) { return; } msg.Emit({{"Msg", msg.desc().name()}, @@ -232,10 +251,20 @@ void GenerateRs(Context msg) { msg.WithDesc(msg.desc().nested_type(i)); GenerateRs(nested_msg); } + }}, + {"oneofs", + [&] { + for (int i = 0; + i < msg.desc().real_oneof_decl_count(); ++i) { + GenerateOneofDefinition( + msg.WithDesc(*msg.desc().real_oneof_decl(i))); + } }}}, R"rs( pub mod $Msg$_ { $nested_msgs$ + + $oneofs$ } // mod $Msg$_ )rs"); }}, @@ -334,6 +363,8 @@ void GenerateRs(Context msg) { } $accessor_fns$ + + $oneof_accessor_fns$ } // impl $Msg$ //~ We implement drop unconditionally, so that `$Msg$: Drop` regardless @@ -348,6 +379,8 @@ void GenerateRs(Context msg) { $Msg_externs$ $accessor_externs$ + + $oneof_externs$ } // extern "C" for $Msg$ $nested_msgs$ @@ -377,29 +410,33 @@ void GenerateThunksCc(Context msg) { } msg.Emit( - { - {"abi", "\"C\""}, // Workaround for syntax highlight bug in VSCode. - {"Msg", msg.desc().name()}, - {"QualifiedMsg", cpp::QualifiedClassName(&msg.desc())}, - {"new_thunk", Thunk(msg, "new")}, - {"delete_thunk", Thunk(msg, "delete")}, - {"serialize_thunk", Thunk(msg, "serialize")}, - {"deserialize_thunk", Thunk(msg, "deserialize")}, - {"nested_msg_thunks", - [&] { - for (int i = 0; i < msg.desc().nested_type_count(); ++i) { - Context nested_msg = - msg.WithDesc(msg.desc().nested_type(i)); - GenerateThunksCc(nested_msg); - } - }}, - {"accessor_thunks", - [&] { - for (int i = 0; i < msg.desc().field_count(); ++i) { - GenerateAccessorThunkCc(msg.WithDesc(*msg.desc().field(i))); - } - }}, - }, + {{"abi", "\"C\""}, // Workaround for syntax highlight bug in VSCode. + {"Msg", msg.desc().name()}, + {"QualifiedMsg", cpp::QualifiedClassName(&msg.desc())}, + {"new_thunk", Thunk(msg, "new")}, + {"delete_thunk", Thunk(msg, "delete")}, + {"serialize_thunk", Thunk(msg, "serialize")}, + {"deserialize_thunk", Thunk(msg, "deserialize")}, + {"nested_msg_thunks", + [&] { + for (int i = 0; i < msg.desc().nested_type_count(); ++i) { + Context nested_msg = + msg.WithDesc(msg.desc().nested_type(i)); + GenerateThunksCc(nested_msg); + } + }}, + {"accessor_thunks", + [&] { + for (int i = 0; i < msg.desc().field_count(); ++i) { + GenerateAccessorThunkCc(msg.WithDesc(*msg.desc().field(i))); + } + }}, + {"oneof_thunks", + [&] { + for (int i = 0; i < msg.desc().real_oneof_decl_count(); ++i) { + GenerateOneofThunkCc(msg.WithDesc(*msg.desc().real_oneof_decl(i))); + } + }}}, R"cc( //~ $abi$ is a workaround for a syntax highlight bug in VSCode. However, //~ that confuses clang-format (it refuses to keep the newline after @@ -417,6 +454,8 @@ void GenerateThunksCc(Context msg) { } $accessor_thunks$ + + $oneof_thunks$ } // extern $abi$ // clang-format on diff --git a/src/google/protobuf/compiler/rust/naming.cc b/src/google/protobuf/compiler/rust/naming.cc index ee2cb924b2..f686bcf877 100644 --- a/src/google/protobuf/compiler/rust/naming.cc +++ b/src/google/protobuf/compiler/rust/naming.cc @@ -36,7 +36,6 @@ #include "absl/strings/str_cat.h" #include "absl/strings/str_replace.h" #include "absl/strings/string_view.h" -#include "absl/strings/strip.h" #include "absl/strings/substitute.h" #include "google/protobuf/compiler/code_generator.h" #include "google/protobuf/compiler/rust/context.h" @@ -83,32 +82,51 @@ std::string GetHeaderFile(Context file) { return absl::StrCat(basename, ".proto.h"); } -std::string Thunk(Context field, absl::string_view op) { +namespace { + +template +std::string Thunk(Context field, absl::string_view op) { // NOTE: When field.is_upb(), this functions outputs must match the symbols // that the upbc plugin generates exactly. Failure to do so correctly results // in a link-time failure. - absl::string_view prefix = field.is_cpp() ? "__rust_proto_thunk__" : ""; std::string thunk = absl::StrCat(prefix, GetUnderscoreDelimitedFullName( field.WithDesc(field.desc().containing_type()))); + absl::string_view format; if (field.is_upb() && op == "get") { - absl::SubstituteAndAppend(&thunk, "_$0", field.desc().name()); + // upb getter is simply the field name (no "get" in the name). + format = "_$1"; + } else if (field.is_upb() && op == "case") { + // upb oneof case function is x_case compared to has/set/clear which are in + // the other order e.g. clear_x. + format = "_$1_$0"; } else { - absl::SubstituteAndAppend(&thunk, "_$0_$1", op, field.desc().name()); + format = "_$0_$1"; } + absl::SubstituteAndAppend(&thunk, format, op, field.desc().name()); return thunk; } +} // namespace + +std::string Thunk(Context field, absl::string_view op) { + return Thunk(field, op); +} + +std::string Thunk(Context field, absl::string_view op) { + return Thunk(field, op); +} + std::string Thunk(Context msg, absl::string_view op) { absl::string_view prefix = msg.is_cpp() ? "__rust_proto_thunk__" : ""; return absl::StrCat(prefix, GetUnderscoreDelimitedFullName(msg), "_", op); } -absl::string_view PrimitiveRsTypeName(Context field) { - switch (field.desc().type()) { +std::string PrimitiveRsTypeName(const FieldDescriptor& desc) { + switch (desc.type()) { case FieldDescriptor::TYPE_BOOL: return "bool"; case FieldDescriptor::TYPE_INT32: @@ -134,7 +152,7 @@ absl::string_view PrimitiveRsTypeName(Context field) { default: break; } - ABSL_LOG(FATAL) << "Unsupported field type: " << field.desc().type_name(); + ABSL_LOG(FATAL) << "Unsupported field type: " << desc.type_name(); return ""; } @@ -173,6 +191,7 @@ std::string FieldInfoComment(Context field) { return comment; } + } // namespace rust } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/rust/naming.h b/src/google/protobuf/compiler/rust/naming.h index 757e08df57..311c512b83 100644 --- a/src/google/protobuf/compiler/rust/naming.h +++ b/src/google/protobuf/compiler/rust/naming.h @@ -49,9 +49,11 @@ std::string GetThunkCcFile(Context file); std::string GetHeaderFile(Context file); std::string Thunk(Context field, absl::string_view op); +std::string Thunk(Context field, absl::string_view op); + std::string Thunk(Context msg, absl::string_view op); -absl::string_view PrimitiveRsTypeName(Context field); +std::string PrimitiveRsTypeName(const FieldDescriptor& desc); std::string FieldInfoComment(Context field); @@ -59,6 +61,7 @@ std::string RustModule(Context msg); std::string RustInternalModuleName(Context file); std::string GetCrateRelativeQualifiedPath(Context msg); + } // namespace rust } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/rust/oneof.cc b/src/google/protobuf/compiler/rust/oneof.cc new file mode 100644 index 0000000000..6966901e97 --- /dev/null +++ b/src/google/protobuf/compiler/rust/oneof.cc @@ -0,0 +1,250 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2023 Google LLC. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google LLC. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "google/protobuf/compiler/rust/oneof.h" + +#include + +#include "absl/strings/string_view.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 { +namespace protobuf { +namespace compiler { +namespace rust { + +// We emit three Rust enums: +// - An enum acting as a tagged union that has each case holds a View<> of +// each of the cases. Named as the one_of name in CamelCase. +// - An enum acting as a tagged union that has each case holds a Mut<> of +// each of the cases. Named as one_of name in CamelCase with "Mut" appended. +// [TODO(b/285309334): Mut not implemented yet]. +// - A simple enum whose cases have int values matching the cpp or upb's +// case enum. Named as the one_of camelcase with "Case" appended. +// All three contain cases matching the fields in the oneof CamelCased. +// The first and second are exposed in the API, the third is internal and +// used for interop with the Kernels in the generation of the other two. +// +// Example: +// For this oneof: +// message SomeMsg { +// oneof some_oneof { +// int32 field_a = 7; +// uint32 field_b = 9; +// } +// } +// +// This will emit as the exposed API: +// pub mod SomeMsg_ { +// // The 'view' struct (no suffix on the name) +// pub enum SomeOneof { +// FieldA(i32) = 7, +// FieldB(u32) = 9, +// not_set = 0 +// } +// } +// impl SomeMsg { +// pub fn some_oneof() -> SomeOneof {...} +// } +// +// 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 +// cpp and upb generate). This enum is pub(super) which makes it private to the +// codegen since its inside an inner mod: +// +// #[repr(C)] pub(super) enum SomeOneofCase { +// FieldA = 7, +// FieldB = 9, +// not_set = 0 +// } + +namespace { +std::string ToCamelCase(absl::string_view name) { + return cpp::UnderscoresToCamelCase(name, /* upper initial letter */ true); +} + +std::string oneofViewEnumRsName(const OneofDescriptor& desc) { + return ToCamelCase(desc.name()); +} + +std::string oneofCaseEnumName(const OneofDescriptor& desc) { + // 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 oneofViewEnumRsName(desc) + "Case"; +} + +// TODO(b/285309334): Promote up to naming.h once all types can be spelled. +std::string RsTypeName(const FieldDescriptor& desc) { + switch (desc.type()) { + case FieldDescriptor::TYPE_MESSAGE: + case FieldDescriptor::TYPE_ENUM: + case FieldDescriptor::TYPE_GROUP: + case FieldDescriptor::TYPE_STRING: + case FieldDescriptor::TYPE_BYTES: + return ""; + default: + return PrimitiveRsTypeName(desc); + } +} + +} // namespace + +void GenerateOneofDefinition(Context oneof) { + const auto& desc = oneof.desc(); + + oneof.Emit( + {{"view_enum_name", oneofViewEnumRsName(desc)}, + {"view_fields", + [&] { + for (int i = 0; i < desc.field_count(); ++i) { + const auto& field = desc.field(i); + std::string rs_type = RsTypeName(*field); + if (rs_type.empty()) { + continue; + } + oneof.Emit({{"name", ToCamelCase(field->name())}, + {"type", rs_type}, + {"number", std::to_string(field->number())}}, + R"rs($name$($type$) = $number$, + )rs"); + } + }}}, + // TODO(b/297342638): Revisit if isize is the optimal repr for this enum. + R"rs( + #[non_exhaustive] + #[derive(Debug, PartialEq)] + #[repr(isize)] + pub enum $view_enum_name$ { + $view_fields$ + + #[allow(non_camel_case_types)] + not_set = 0 + } + + )rs"); + + // 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. + oneof.Emit({{"case_enum_name", oneofCaseEnumName(desc)}, + {"cases", + [&] { + for (int i = 0; i < desc.field_count(); ++i) { + const auto& field = desc.field(i); + oneof.Emit({{"name", ToCamelCase(field->name())}, + {"number", std::to_string(field->number())}}, + R"rs($name$ = $number$, + )rs"); + } + }}}, + R"rs( + #[repr(C)] + #[derive(Debug, Copy, Clone, PartialEq)] + pub(super) enum $case_enum_name$ { + $cases$ + + #[allow(non_camel_case_types)] + not_set = 0 + } + + )rs"); +} + +void GenerateOneofAccessors(Context oneof) { + const auto& desc = oneof.desc(); + + oneof.Emit( + {{"oneof_name", desc.name()}, + {"view_enum_name", oneofViewEnumRsName(desc)}, + {"case_enum_name", oneofCaseEnumName(desc)}, + {"cases", + [&] { + for (int i = 0; i < desc.field_count(); ++i) { + const auto& field = desc.field(i); + std::string rs_type = RsTypeName(*field); + if (rs_type.empty()) { + continue; + } + oneof.Emit( + { + {"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$()), + )rs"); + } + }}, + {"case_thunk", Thunk(oneof, "case")}}, + R"rs( + pub fn r#$oneof_name$(&self) -> $Msg$_::$view_enum_name$ { + match unsafe { $case_thunk$(self.inner.msg) } { + $cases$ + _ => $Msg$_::$view_enum_name$::not_set + } + } + + )rs"); +} + +void GenerateOneofExternC(Context oneof) { + const auto& desc = oneof.desc(); + oneof.Emit( + { + {"case_enum_rs_name", oneofCaseEnumName(desc)}, + {"case_thunk", Thunk(oneof, "case")}, + }, + R"rs( + fn $case_thunk$(raw_msg: $pbi$::RawMessage) -> $Msg$_::$case_enum_rs_name$; + )rs"); +} + +void GenerateOneofThunkCc(Context oneof) { + const auto& desc = oneof.desc(); + oneof.Emit( + { + {"oneof_name", desc.name()}, + {"case_enum_name", oneofCaseEnumName(desc)}, + {"case_thunk", Thunk(oneof, "case")}, + {"QualifiedMsg", cpp::QualifiedClassName(desc.containing_type())}, + }, + R"cc( + $QualifiedMsg$::$case_enum_name$ $case_thunk$($QualifiedMsg$* msg) { + return msg->$oneof_name$_case(); + } + )cc"); +} + +} // namespace rust +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/rust/oneof.h b/src/google/protobuf/compiler/rust/oneof.h new file mode 100644 index 0000000000..f7ac2d0b17 --- /dev/null +++ b/src/google/protobuf/compiler/rust/oneof.h @@ -0,0 +1,52 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2023 Google LLC. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google LLC. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef GOOGLE_PROTOBUF_COMPILER_RUST_ONEOF_H__ +#define GOOGLE_PROTOBUF_COMPILER_RUST_ONEOF_H__ + +#include "google/protobuf/compiler/rust/context.h" +#include "google/protobuf/descriptor.h" + +namespace google { +namespace protobuf { +namespace compiler { +namespace rust { + +void GenerateOneofDefinition(Context oneof); +void GenerateOneofAccessors(Context oneof); +void GenerateOneofExternC(Context oneof); +void GenerateOneofThunkCc(Context oneof); + +} // namespace rust +} // namespace compiler +} // namespace protobuf +} // namespace google + +#endif // GOOGLE_PROTOBUF_COMPILER_RUST_ONEOF_H__