Factor out different codegen parts of the Rust backend.

The intent of this change is to take the best ideas from the C++ backend, such as having generator objects that can cache pre-computed state, while minimizing duplication.

Where possible, we take the approach of making the C++ and UPB kernel-specific code as similar as possible, since this reduces the number of templates we need to keep in sync.

PiperOrigin-RevId: 524903073
pull/12484/head
Miguel Young de la Sota 2 years ago committed by Copybara-Service
parent 01aca3e606
commit 6678619d10
  1. 2
      .github/workflows/codespell.yml
  2. 5
      src/google/protobuf/compiler/cpp/BUILD.bazel
  3. 49
      src/google/protobuf/compiler/rust/BUILD.bazel
  4. 67
      src/google/protobuf/compiler/rust/accessors/accessors.cc
  5. 103
      src/google/protobuf/compiler/rust/accessors/accessors.h
  6. 128
      src/google/protobuf/compiler/rust/accessors/singular_bytes.cc
  7. 124
      src/google/protobuf/compiler/rust/accessors/singular_scalar.cc
  8. 13
      src/google/protobuf/compiler/rust/context.h
  9. 364
      src/google/protobuf/compiler/rust/generator.cc
  10. 302
      src/google/protobuf/compiler/rust/message.cc
  11. 65
      src/google/protobuf/compiler/rust/message.h
  12. 33
      src/google/protobuf/compiler/rust/naming.cc
  13. 2
      src/google/protobuf/compiler/rust/naming.h

@ -24,4 +24,4 @@ jobs:
with:
check_filenames: true
skip: ./.git,./third_party,./conformance/third_party,*.snk,*.pb,*.pb.cc,*.pb.h,*.pb.cs,*-upb.h,*-upb.c,./src/google/protobuf/testdata,./objectivec/Tests,./python/compatibility_tests/v2.5.0/tests/google/protobuf/internal,./.github/workflows/codespell.yml
ignore_words_list: "alow,alse,atleast,ba,chec,cleare,copyable,couldn,cloneable,crate,dedup,dur,errorprone,falsy,files',fo,fundementals,hel,importd,inherting,inout,leapyear,nd,nin,ois,ons,parseable,process',ro,te,testof,ue,unparseable,wasn,wee,gae,keyserver,objext,od,optin,streem,sur,falsy,cleary"
ignore_words_list: "alow,alse,atleast,ba,chec,cleare,copyable,couldn,cloneable,crate,dedup,dur,errorprone,falsy,files',fo,fundementals,generaters,hel,importd,inherting,inout,leapyear,nd,nin,ois,ons,parseable,process',ro,te,testof,ue,unparseable,wasn,wee,gae,keyserver,objext,od,optin,streem,sur,falsy,cleary"

@ -33,7 +33,10 @@ cc_library(
],
copts = COPTS,
include_prefix = "google/protobuf/compiler/cpp",
visibility = ["//pkg:__pkg__"],
visibility = [
"//pkg:__pkg__",
"//src/google/protobuf/compiler/rust:__subpackages__",
],
deps = [
"//src/google/protobuf:protobuf_nowkt",
"//src/google/protobuf/compiler:code_generator",

@ -17,30 +17,67 @@ cc_library(
],
deps = [
":context",
":message",
":naming",
":upb_kernel",
"//src/google/protobuf:protobuf_nowkt",
"//src/google/protobuf/compiler:code_generator",
"//src/google/protobuf/compiler/cpp:names",
"@com_google_absl//absl/algorithm:container",
"@com_google_absl//absl/log:absl_check",
"@com_google_absl//absl/types:optional",
],
)
cc_library(
name = "message",
srcs = ["message.cc"],
hdrs = ["message.h"],
copts = COPTS,
include_prefix = "google/protobuf/compiler/rust",
deps = [
":context",
":naming",
"//src/google/protobuf:protobuf_nowkt",
"//src/google/protobuf/compiler/cpp:names",
":accessors",
"@com_google_absl//absl/log:absl_check",
"@com_google_absl//absl/log:absl_log",
],
)
cc_library(
name = "accessors",
srcs = [
"accessors/accessors.cc",
"accessors/singular_bytes.cc",
"accessors/singular_scalar.cc",
],
hdrs = ["accessors/accessors.h"],
copts = COPTS,
include_prefix = "google/protobuf/compiler/rust",
deps = [
"//src/google/protobuf:protobuf_nowkt",
"//src/google/protobuf/compiler/cpp:names_internal",
":context",
":naming",
"@com_google_absl//absl/log:absl_check",
"@com_google_absl//absl/strings",
],
)
cc_library(
name = "context",
srcs = ["context.cc"],
hdrs = ["context.h"],
copts = COPTS,
include_prefix = "google/protobuf/compiler/rust",
visibility = [
"//pkg:__pkg__",
"//src/google/protobuf/compiler:__pkg__",
],
deps = [
"//src/google/protobuf/compiler:code_generator",
"//src/google/protobuf/io:printer",
"@com_google_absl//absl/algorithm:container",
"@com_google_absl//absl/log:absl_log",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
@ -54,10 +91,6 @@ cc_library(
hdrs = ["naming.h"],
copts = COPTS,
include_prefix = "google/protobuf/compiler/rust",
visibility = [
"//pkg:__pkg__",
"//src/google/protobuf/compiler:__pkg__",
],
deps = [
":context",
"//src/google/protobuf:protobuf_nowkt",

@ -0,0 +1,67 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google Inc. 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 Inc. 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/accessors/accessors.h"
#include <memory>
#include "google/protobuf/compiler/rust/context.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/descriptor.pb.h"
namespace google {
namespace protobuf {
namespace compiler {
namespace rust {
std::unique_ptr<AccessorGenerator> AccessorGenerator::For(
Context<FieldDescriptor> field) {
// We do not support [ctype=FOO] (used to set the field type in C++ to
// cord or string_piece) in V0 API.
if (field.desc().options().has_ctype()) {
return nullptr;
}
switch (field.desc().type()) {
case FieldDescriptor::TYPE_INT64:
case FieldDescriptor::TYPE_BOOL:
if (field.desc().is_repeated()) return nullptr;
return ForSingularScalar(field);
case FieldDescriptor::TYPE_BYTES:
if (field.desc().is_repeated() || field.is_upb()) return nullptr;
return ForSingularBytes(field);
default:
return nullptr;
}
}
} // namespace rust
} // namespace compiler
} // namespace protobuf
} // namespace google

@ -0,0 +1,103 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google Inc. 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 Inc. 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_ACCESSORS_ACCESSORS_H__
#define GOOGLE_PROTOBUF_COMPILER_RUST_ACCESSORS_ACCESSORS_H__
#include <memory>
#include "absl/log/absl_check.h"
#include "google/protobuf/compiler/rust/context.h"
#include "google/protobuf/descriptor.h"
namespace google {
namespace protobuf {
namespace compiler {
namespace rust {
class AccessorGenerator {
public:
AccessorGenerator() = default;
AccessorGenerator(const AccessorGenerator &) = delete;
AccessorGenerator(AccessorGenerator &&) = delete;
AccessorGenerator &operator=(const AccessorGenerator &) = delete;
AccessorGenerator &operator=(AccessorGenerator &&) = delete;
virtual ~AccessorGenerator();
// Constructs a generator for the given field.
//
// Returns `nullptr` if there is no known generator for this field.
static std::unique_ptr<AccessorGenerator> For(Context<FieldDescriptor> field);
void GenerateMsgImpl(Context<FieldDescriptor> field) const {
InMsgImpl(field);
}
void GenerateExternC(Context<FieldDescriptor> field) const {
InExternC(field);
}
void GenerateThunkCc(Context<FieldDescriptor> field) const {
ABSL_CHECK(field.is_cpp());
InThunkCc(field);
}
private:
// Note: the virtual functions are duplicated as non-virtual public functions,
// so that we can customize prologue and epilogue behavior for these
// functions. For example, consider calling `field.printer.WithVars()` as a
// prologue to inject variables automatically.
// Called inside the main inherent `impl Msg {}` block.
virtual void InMsgImpl(Context<FieldDescriptor> field) const {}
// Called inside of a message's `extern "C" {}` block.
virtual void InExternC(Context<FieldDescriptor> field) const {}
// Called inside of an `extern "C" {}` block in the `.thunk.cc` file, if such
// a file is being generated.
virtual void InThunkCc(Context<FieldDescriptor> field) const {}
// These static factories are defined in the corresponding implementation
// files for each implementation of AccessorGenerator.
static std::unique_ptr<AccessorGenerator> ForSingularScalar(
Context<FieldDescriptor> field);
static std::unique_ptr<AccessorGenerator> ForSingularBytes(
Context<FieldDescriptor> field);
};
inline AccessorGenerator::~AccessorGenerator() = default;
} // namespace rust
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_RUST_ACCESSORS_ACCESSORS_H__

@ -0,0 +1,128 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google Inc. 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 Inc. 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 <memory>
#include "absl/strings/string_view.h"
#include "google/protobuf/compiler/cpp/helpers.h"
#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/descriptor.h"
namespace google {
namespace protobuf {
namespace compiler {
namespace rust {
namespace {
class SingularBytes final : public AccessorGenerator {
public:
~SingularBytes() override = default;
void InMsgImpl(Context<FieldDescriptor> field) const override {
field.Emit(
{
{"field", field.desc().name()},
{"hazzer_thunk", GetAccessorThunkName(field, "has")},
{"getter_thunk", GetAccessorThunkName(field, "")},
{"setter_thunk", GetAccessorThunkName(field, "set")},
{"clearer_thunk", GetAccessorThunkName(field, "clear")},
},
R"rs(
pub fn $field$(&self) -> Option<&[u8]> {
if !unsafe { $hazzer_thunk$(self.msg) } {
return None;
}
unsafe {
let val = $getter_thunk$(self.msg);
Some($std$::slice::from_raw_parts(val.ptr, val.len))
}
}
pub fn $field$_set(&mut self, val: Option<&[u8]>) {
match val {
Some(val) => unsafe { $setter_thunk$(self.msg, val.as_ptr(), val.len()) },
None => unsafe { $clearer_thunk$(self.msg) },
}
}
)rs");
}
void InExternC(Context<FieldDescriptor> field) const override {
field.Emit(
{
{"hazzer_thunk", GetAccessorThunkName(field, "has")},
{"getter_thunk", GetAccessorThunkName(field, "")},
{"setter_thunk", GetAccessorThunkName(field, "set")},
{"clearer_thunk", GetAccessorThunkName(field, "clear")},
},
R"rs(
fn $hazzer_thunk$(raw_msg: $NonNull$<u8>) -> bool;
fn $getter_thunk$(raw_msg: $NonNull$<u8>) -> $pb$::PtrAndLen;
fn $setter_thunk$(raw_msg: $NonNull$<u8>, val: *const u8, len: usize);
fn $clearer_thunk$(raw_msg: $NonNull$<u8>);
)rs");
}
void InThunkCc(Context<FieldDescriptor> field) const override {
field.Emit(
{
{"field", field.desc().name()},
{"namespace", cpp::Namespace(field.desc().containing_type())},
{"hazzer_thunk", GetAccessorThunkName(field, "has")},
{"getter_thunk", GetAccessorThunkName(field, "")},
{"setter_thunk", GetAccessorThunkName(field, "set")},
{"clearer_thunk", GetAccessorThunkName(field, "clear")},
},
R"cc(
bool $hazzer_thunk$($namespace$::$Msg$* msg) {
return msg->has_$field$();
}
::google::protobuf::rust_internal::PtrAndLen $getter_thunk$($namespace$::$Msg$* msg) {
absl::string_view val = msg->$field$();
return google::protobuf::rust_internal::PtrAndLen(val.data(), val.size());
}
void $setter_thunk$($namespace$::$Msg$* msg, const char* ptr,
::std::size_t size) {
msg->set_$field$(absl::string_view(ptr, size));
}
void $clearer_thunk$($namespace$::$Msg$* msg) { msg->clear_$field$(); }
)cc");
}
};
} // namespace
std::unique_ptr<AccessorGenerator> AccessorGenerator::ForSingularBytes(
Context<FieldDescriptor> field) {
return std::make_unique<SingularBytes>();
}
} // namespace rust
} // namespace compiler
} // namespace protobuf
} // namespace google

@ -0,0 +1,124 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google Inc. 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 Inc. 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 <memory>
#include "absl/strings/string_view.h"
#include "google/protobuf/compiler/cpp/helpers.h"
#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/descriptor.h"
namespace google {
namespace protobuf {
namespace compiler {
namespace rust {
namespace {
class SingularScalar final : public AccessorGenerator {
public:
~SingularScalar() override = default;
void InMsgImpl(Context<FieldDescriptor> field) const override {
field.Emit(
{
{"field", field.desc().name()},
{"Scalar", PrimitiveRsTypeName(field)},
{"hazzer_thunk", GetAccessorThunkName(field, "has")},
{"getter_thunk", GetAccessorThunkName(field, "")},
{"setter_thunk", GetAccessorThunkName(field, "set")},
{"clearer_thunk", GetAccessorThunkName(field, "clear")},
},
R"rs(
pub fn $field$(&self) -> Option<$Scalar$> {
if !unsafe { $hazzer_thunk$(self.msg) } {
return None;
}
Some(unsafe { $getter_thunk$(self.msg) })
}
pub fn $field$_set(&mut self, val: Option<$Scalar$>) {
match val {
Some(val) => unsafe { $setter_thunk$(self.msg, val) },
None => unsafe { $clearer_thunk$(self.msg) },
}
}
)rs");
}
void InExternC(Context<FieldDescriptor> field) const override {
field.Emit(
{
{"Scalar", PrimitiveRsTypeName(field)},
{"hazzer_thunk", GetAccessorThunkName(field, "has")},
{"getter_thunk", GetAccessorThunkName(field, "")},
{"setter_thunk", GetAccessorThunkName(field, "set")},
{"clearer_thunk", GetAccessorThunkName(field, "clear")},
},
R"rs(
fn $hazzer_thunk$(raw_msg: $NonNull$<u8>) -> bool;
fn $getter_thunk$(raw_msg: $NonNull$<u8>) -> $Scalar$;
fn $setter_thunk$(raw_msg: $NonNull$<u8>, val: $Scalar$);
fn $clearer_thunk$(raw_msg: $NonNull$<u8>);
)rs");
}
void InThunkCc(Context<FieldDescriptor> field) const override {
field.Emit(
{
{"field", field.desc().name()},
{"Scalar", cpp::PrimitiveTypeName(field.desc().cpp_type())},
{"namespace", cpp::Namespace(field.desc().containing_type())},
{"hazzer_thunk", GetAccessorThunkName(field, "has")},
{"getter_thunk", GetAccessorThunkName(field, "")},
{"setter_thunk", GetAccessorThunkName(field, "set")},
{"clearer_thunk", GetAccessorThunkName(field, "clear")},
},
R"cc(
bool $hazzer_thunk$($namespace$::$Msg$* msg) {
return msg->has_$field$();
}
$Scalar$ $getter_thunk$($namespace$::$Msg$* msg) { return msg->$field$(); }
void $setter_thunk$($namespace$::$Msg$* msg, $Scalar$ val) {
msg->set_$field$(val);
}
void $clearer_thunk$($namespace$::$Msg$* msg) { msg->clear_$field$(); }
)cc");
}
};
} // namespace
std::unique_ptr<AccessorGenerator> AccessorGenerator::ForSingularScalar(
Context<FieldDescriptor> field) {
return std::make_unique<SingularScalar>();
}
} // namespace rust
} // namespace compiler
} // namespace protobuf
} // namespace google

@ -31,6 +31,7 @@
#ifndef GOOGLE_PROTOBUF_COMPILER_RUST_CONTEXT_H__
#define GOOGLE_PROTOBUF_COMPILER_RUST_CONTEXT_H__
#include "absl/log/absl_log.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
@ -46,6 +47,18 @@ enum class Kernel {
kCpp,
};
inline absl::string_view KernelRsName(Kernel kernel) {
switch (kernel) {
case Kernel::kUpb:
return "upb";
case Kernel::kCpp:
return "cpp";
default:
ABSL_LOG(FATAL) << "Unknown kernel type: " << static_cast<int>(kernel);
return "";
}
}
// Global options for a codegen invocation.
struct Options {
Kernel kernel;

@ -30,30 +30,29 @@
#include "google/protobuf/compiler/rust/generator.h"
#include <memory>
#include <string>
#include <vector>
#include "absl/log/absl_log.h"
#include "absl/algorithm/container.h"
#include "absl/memory/memory.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_join.h"
#include "absl/strings/str_replace.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "absl/strings/substitute.h"
#include "google/protobuf/compiler/code_generator.h"
#include "google/protobuf/compiler/cpp/helpers.h"
#include "google/protobuf/compiler/cpp/names.h"
#include "google/protobuf/compiler/rust/context.h"
#include "google/protobuf/compiler/rust/message.h"
#include "google/protobuf/compiler/rust/naming.h"
#include "google/protobuf/compiler/rust/upb_kernel.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/descriptor.pb.h"
#include "google/protobuf/io/printer.h"
#include "google/protobuf/io/zero_copy_stream.h"
namespace google {
namespace protobuf {
namespace compiler {
namespace rust {
namespace {
void EmitOpeningOfPackageModules(Context<FileDescriptor> file) {
if (file.desc().package().empty()) return;
for (absl::string_view segment : absl::StrSplit(file.desc().package(), '.')) {
@ -69,291 +68,14 @@ void EmitClosingOfPackageModules(Context<FileDescriptor> file) {
std::vector<absl::string_view> segments =
absl::StrSplit(file.desc().package(), '.');
absl::c_reverse(segments);
for (absl::string_view segment : segments) {
file.Emit({{"segment", segment}},
R"rs(
} // mod $segment$
)rs");
}
}
void EmitGetterExpr(Context<FieldDescriptor> field) {
std::string thunk_name = GetAccessorThunkName(field, "get");
switch (field.desc().type()) {
case FieldDescriptor::TYPE_BYTES:
field.Emit({{"getter_thunk_name", thunk_name}},
R"rs(
let val = unsafe { $getter_thunk_name$(self.msg) };
Some(unsafe { ::__std::slice::from_raw_parts(val.ptr, val.len) })
)rs");
return;
default:
field.Emit({{"getter_thunk_name", thunk_name}},
R"rs(
Some(unsafe { $getter_thunk_name$(self.msg) })
)rs");
}
}
void GenerateAccessorFns(Context<Descriptor> msg) {
for (int i = 0; i < msg.desc().field_count(); ++i) {
auto field = msg.WithDesc(msg.desc().field(i));
if (!IsSupportedFieldType(field)) {
continue;
}
field.Emit(
{
{"field_name", field.desc().name()},
{"FieldType", PrimitiveRsTypeName(field)},
{"hazzer_thunk_name", GetAccessorThunkName(field, "has")},
{"getter_thunk_name", GetAccessorThunkName(field, "get")},
{"getter_expr", [&] { EmitGetterExpr(field); }},
{"setter_thunk_name", GetAccessorThunkName(field, "set")},
{"setter_args",
[&] {
switch (field.desc().type()) {
case FieldDescriptor::TYPE_BYTES:
field.Emit("val.as_ptr(), val.len()");
return;
default:
field.Emit("val");
}
}},
{"clearer_thunk_name", GetAccessorThunkName(field, "clear")},
},
R"rs(
pub fn $field_name$(&self) -> Option<$FieldType$> {
if !unsafe { $hazzer_thunk_name$(self.msg) } {
return None;
}
$getter_expr$
}
pub fn $field_name$_set(&mut self, val: Option<$FieldType$>) {
match val {
Some(val) => unsafe { $setter_thunk_name$(self.msg, $setter_args$) },
None => unsafe { $clearer_thunk_name$(self.msg) },
}
}
)rs");
}
}
void GenerateAccessorThunkRsDeclarations(Context<Descriptor> msg) {
for (int i = 0; i < msg.desc().field_count(); ++i) {
auto field = msg.WithDesc(msg.desc().field(i));
if (!IsSupportedFieldType(field)) {
continue;
}
absl::string_view type_name = PrimitiveRsTypeName(field);
field.Emit(
{
{"FieldType", type_name},
{"GetterReturnType",
[&] {
switch (field.desc().type()) {
case FieldDescriptor::TYPE_BYTES:
field.Emit("::__pb::PtrAndLen");
return;
default:
field.Emit(type_name);
}
}},
{"hazzer_thunk_name", GetAccessorThunkName(field, "has")},
{"getter_thunk_name", GetAccessorThunkName(field, "get")},
{"setter_thunk_name", GetAccessorThunkName(field, "set")},
{"setter_params",
[&] {
switch (field.desc().type()) {
case FieldDescriptor::TYPE_BYTES:
field.Emit("val: *const u8, len: usize");
return;
default:
field.Emit({{"type_name", type_name}}, "val: $type_name$");
}
}},
{"clearer_thunk_name", GetAccessorThunkName(field, "clear")},
},
R"rs(
fn $hazzer_thunk_name$(raw_msg: ::__std::ptr::NonNull<u8>) -> bool;
fn $getter_thunk_name$(raw_msg: ::__std::ptr::NonNull<u8>) -> $GetterReturnType$;;
fn $setter_thunk_name$(raw_msg: ::__std::ptr::NonNull<u8>, $setter_params$);
fn $clearer_thunk_name$(raw_msg: ::__std::ptr::NonNull<u8>);
)rs");
}
}
void GenerateAccessorThunksCcDefinitions(Context<Descriptor> msg) {
for (int i = 0; i < msg.desc().field_count(); ++i) {
auto field = msg.WithDesc(msg.desc().field(i));
if (!IsSupportedFieldType(field)) {
continue;
}
absl::string_view type_name =
cpp::PrimitiveTypeName(field.desc().cpp_type());
field.Emit(
{{"field_name", field.desc().name()},
{"FieldType", type_name},
{"GetterReturnType",
[&] {
switch (field.desc().type()) {
case FieldDescriptor::TYPE_BYTES:
field.Emit("::google::protobuf::rust_internal::PtrAndLen");
return;
default:
field.Emit(type_name);
}
}},
{"namespace", cpp::Namespace(&field.desc())},
{"hazzer_thunk_name", GetAccessorThunkName(field, "has")},
{"getter_thunk_name", GetAccessorThunkName(field, "get")},
{"getter_body",
[&] {
switch (field.desc().type()) {
case FieldDescriptor::TYPE_BYTES:
field.Emit({{"field_name", field.desc().name()}}, R"cc(
absl::string_view val = msg->$field_name$();
return google::protobuf::rust_internal::PtrAndLen(val.data(), val.size());
)cc");
return;
default:
field.Emit(R"cc(return msg->$field_name$();)cc");
}
}},
{"setter_thunk_name", GetAccessorThunkName(field, "set")},
{"setter_params",
[&] {
switch (field.desc().type()) {
case FieldDescriptor::TYPE_BYTES:
field.Emit("const char* ptr, size_t size");
return;
default:
field.Emit({{"type_name", type_name}}, "$type_name$ val");
}
}},
{"setter_args",
[&] {
switch (field.desc().type()) {
case FieldDescriptor::TYPE_BYTES:
field.Emit("absl::string_view(ptr, size)");
return;
default:
field.Emit("val");
}
}},
{"clearer_thunk_name", GetAccessorThunkName(field, "clear")}},
R"cc(
extern "C" {
bool $hazzer_thunk_name$($namespace$::$Msg$* msg) {
return msg->has_$field_name$();
}
$GetterReturnType$ $getter_thunk_name$($namespace$::$Msg$* msg) {
$getter_body$
}
void $setter_thunk_name$($namespace$::$Msg$* msg, $setter_params$) {
msg->set_$field_name$($setter_args$);
}
void $clearer_thunk_name$($namespace$::$Msg$* msg) {
msg->clear_$field_name$();
}
}
)cc");
}
}
void GenerateForCpp(Context<FileDescriptor> file) {
for (int i = 0; i < file.desc().message_type_count(); ++i) {
auto msg = file.WithDesc(file.desc().message_type(i));
msg.Emit(
{
{"Msg", msg.desc().name()},
{"pkg_Msg", GetUnderscoreDelimitedFullName(msg)},
{"accessor_fns", [&] { GenerateAccessorFns(msg); }},
{"accessor_thunks",
[&] { GenerateAccessorThunkRsDeclarations(msg); }},
},
R"rs(
#[allow(non_camel_case_types)]
pub struct $Msg$ {
msg: ::__std::ptr::NonNull<u8>,
}
impl $Msg$ {
pub fn new() -> Self {
Self {
msg: unsafe { __rust_proto_thunk__$pkg_Msg$__new() }
}
}
pub fn serialize(&self) -> ::__pb::SerializedData {
return unsafe { __rust_proto_thunk__$pkg_Msg$__serialize(self.msg) };
}
pub fn __unstable_cpp_repr_grant_permission_to_break(&mut self) -> ::__std::ptr::NonNull<u8> {
self.msg
}
pub fn deserialize(&mut self, data: &[u8]) -> Result<(), ::__pb::ParseError> {
let success = unsafe { __rust_proto_thunk__$pkg_Msg$__deserialize(
self.msg,
::__pb::SerializedData::from_raw_parts(
::__std::ptr::NonNull::new(data.as_ptr() as *mut _).unwrap(),
data.len()))
};
success.then_some(()).ok_or(::__pb::ParseError)
}
$accessor_fns$
}
extern "C" {
fn __rust_proto_thunk__$pkg_Msg$__new() -> ::__std::ptr::NonNull<u8>;
fn __rust_proto_thunk__$pkg_Msg$__serialize(raw_msg: ::__std::ptr::NonNull<u8>) -> ::__pb::SerializedData;
fn __rust_proto_thunk__$pkg_Msg$__deserialize(raw_msg: ::__std::ptr::NonNull<u8>, data: ::__pb::SerializedData) -> bool;
$accessor_thunks$
}
)rs");
}
}
void GenerateThunksForCpp(Context<FileDescriptor> file) {
for (int i = 0; i < file.desc().message_type_count(); ++i) {
auto msg = file.WithDesc(file.desc().message_type(i));
file.Emit(
{
{"Msg", msg.desc().name()},
{"pkg_Msg", GetUnderscoreDelimitedFullName(msg)},
{"namespace", cpp::Namespace(&msg.desc())},
{"accessor_thunks",
[&] { GenerateAccessorThunksCcDefinitions(msg); }},
},
R"cc(
extern "C" {
void* __rust_proto_thunk__$pkg_Msg$__new() { return new $namespace$::$Msg$(); }
google::protobuf::rust_internal::SerializedData
__rust_proto_thunk__$pkg_Msg$__serialize($namespace$::$Msg$* msg) {
return google::protobuf::rust_internal::SerializeMsg(msg);
}
bool __rust_proto_thunk__$pkg_Msg$__deserialize(
$namespace$::$Msg$* msg,
google::protobuf::rust_internal::SerializedData data) {
return msg->ParseFromArray(data.data, data.len);
}
$accessor_thunks$
}
)cc");
}
}
std::string GetKernelRustName(Kernel kernel) {
switch (kernel) {
case Kernel::kUpb:
return "upb";
case Kernel::kCpp:
return "cpp";
for (absl::string_view segment : segments) {
file.Emit({{"segment", segment}}, R"rs(
} // mod $segment$
)rs");
}
ABSL_LOG(FATAL) << "Unknown kernel type: " << static_cast<int>(kernel);
return "";
}
} // namespace
bool RustGenerator::Generate(const FileDescriptor* file_desc,
const std::string& parameter,
@ -371,7 +93,14 @@ bool RustGenerator::Generate(const FileDescriptor* file_desc,
io::Printer printer(outfile.get());
file = file.WithPrinter(&printer);
file.Emit({{"kernel", GetKernelRustName(opts->kernel)}}, R"rs(
// Convenience shorthands for common symbols.
auto v = file.printer().WithVars({
{"std", "::__std"},
{"pb", "::__pb"},
{"NonNull", "::__std::ptr::NonNull"},
});
file.Emit({{"kernel", KernelRsName(file.opts().kernel)}}, R"rs(
extern crate protobuf_$kernel$ as __pb;
extern crate std as __std;
@ -387,33 +116,46 @@ bool RustGenerator::Generate(const FileDescriptor* file_desc,
for (int j = 0; j < dep.desc().message_type_count(); ++j) {
auto msg = file.WithDesc(dep.desc().message_type(j));
// TODO(b/272728844): Implement real logic
file.Emit({{"crate", crate_name},
{"pkg::Msg", GetCrateRelativeQualifiedPath(msg)}},
R"rs(
pub use $crate$::$pkg::Msg$;
)rs");
file.Emit(
{
{"crate", crate_name},
{"pkg::Msg", GetCrateRelativeQualifiedPath(msg)},
},
R"rs(
pub use $crate$::$pkg::Msg$;
)rs");
}
}
switch (opts->kernel) {
case Kernel::kUpb:
rust::UpbKernel().Generate(&file.desc(), printer);
break;
case Kernel::kCpp:
GenerateForCpp(file);
std::unique_ptr<io::ZeroCopyOutputStream> thunks_cc;
std::unique_ptr<io::Printer> thunks_printer;
if (file.is_cpp()) {
thunks_cc.reset(generator_context->Open(GetThunkCcFile(file)));
thunks_printer = std::make_unique<io::Printer>(thunks_cc.get());
auto thunksfile =
absl::WrapUnique(generator_context->Open(GetThunkCcFile(file)));
google::protobuf::io::Printer thunks(thunksfile.get());
auto thunk_file = file.WithPrinter(&thunks);
thunk_file.Emit({{"proto_h", GetHeaderFile(file)}},
R"cc(
thunks_printer->Emit({{"proto_h", GetHeaderFile(file)}},
R"cc(
#include "$proto_h$"
#include "google/protobuf/rust/cpp_kernel/cpp_api.h"
)cc");
GenerateThunksForCpp(thunk_file);
break;
)cc");
}
for (int i = 0; i < file.desc().message_type_count(); ++i) {
auto msg = file.WithDesc(file.desc().message_type(i));
MessageGenerator gen(msg);
gen.GenerateRs(msg);
msg.printer().PrintRaw("\n");
if (file.is_cpp()) {
auto thunks_msg = msg.WithPrinter(thunks_printer.get());
thunks_msg.Emit({{"Msg", msg.desc().full_name()}}, R"cc(
// $Msg$
)cc");
gen.GenerateThunkCc(thunks_msg);
thunks_msg.printer().PrintRaw("\n");
}
}
EmitClosingOfPackageModules(file);
return true;

@ -0,0 +1,302 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google Inc. 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 Inc. 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/message.h"
#include "absl/log/absl_check.h"
#include "absl/log/absl_log.h"
#include "google/protobuf/compiler/cpp/names.h"
#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/descriptor.h"
namespace google {
namespace protobuf {
namespace compiler {
namespace rust {
namespace {
void MessageStructFields(Context<Descriptor> msg) {
switch (msg.opts().kernel) {
case Kernel::kCpp:
msg.Emit(R"rs(
msg: $NonNull$<u8>,
)rs");
return;
case Kernel::kUpb:
msg.Emit(R"rs(
msg: $NonNull$<u8>,
arena: *mut $pb$::Arena,
)rs");
return;
}
ABSL_LOG(FATAL) << "unreachable";
}
void MessageNew(Context<Descriptor> msg) {
switch (msg.opts().kernel) {
case Kernel::kCpp:
msg.Emit(R"rs(
Self { msg: unsafe { __rust_proto_thunk__$pkg_Msg$__new() } }
)rs");
return;
case Kernel::kUpb:
msg.Emit(R"rs(
let arena = unsafe { $pb$::Arena::new() };
let msg = unsafe { $pkg_Msg$_new(arena) };
$Msg$ { msg, arena }
)rs");
return;
}
ABSL_LOG(FATAL) << "unreachable";
}
void MessageSerialize(Context<Descriptor> msg) {
switch (msg.opts().kernel) {
case Kernel::kCpp:
msg.Emit(R"rs(
unsafe { __rust_proto_thunk__$pkg_Msg$__serialize(self.msg) }
)rs");
return;
case Kernel::kUpb:
msg.Emit(R"rs(
let arena = unsafe { $pb$::__runtime::upb_Arena_New() };
let mut len = 0;
unsafe {
let data = $pkg_Msg$_serialize(self.msg, arena, &mut len);
$pb$::SerializedData::from_raw_parts(arena, data, len)
}
)rs");
return;
}
ABSL_LOG(FATAL) << "unreachable";
}
void MessageDeserialize(Context<Descriptor> msg) {
switch (msg.opts().kernel) {
case Kernel::kCpp:
msg.Emit(R"rs(
let success = unsafe {
let data = $pb$::SerializedData::from_raw_parts(
$NonNull$::new(data.as_ptr() as *mut _).unwrap(),
data.len(),
);
__rust_proto_thunk__$pkg_Msg$__deserialize(self.msg, data)
};
success.then_some(()).ok_or($pb$::ParseError)
)rs");
return;
case Kernel::kUpb:
msg.Emit(R"rs(
let _ = data;
$std$::unimplemented!()
)rs");
return;
}
ABSL_LOG(FATAL) << "unreachable";
}
void MessageExterns(Context<Descriptor> msg) {
switch (msg.opts().kernel) {
case Kernel::kCpp:
msg.Emit(R"rs(
fn __rust_proto_thunk__$pkg_Msg$__new() -> $NonNull$<u8>;
fn __rust_proto_thunk__$pkg_Msg$__serialize(raw_msg: $NonNull$<u8>)
-> $pb$::SerializedData;
fn __rust_proto_thunk__$pkg_Msg$__deserialize(
raw_msg: $NonNull$<u8>,
data: $pb$::SerializedData,
) -> bool;
)rs");
return;
case Kernel::kUpb:
msg.Emit(R"rs(
fn $pkg_Msg$_new(arena: *mut $pb$::Arena) -> $NonNull$<u8>;
fn $pkg_Msg$_serialize(
msg: $NonNull$<u8>,
arena: *mut $pb$::Arena,
len: &mut usize) -> $NonNull$<u8>;
)rs");
return;
}
ABSL_LOG(FATAL) << "unreachable";
}
} // namespace
MessageGenerator::MessageGenerator(Context<Descriptor> msg) {
accessors_.resize(msg.desc().field_count());
for (int i = 0; i < msg.desc().field_count(); ++i) {
auto field = msg.WithDesc(msg.desc().field(i));
accessors_[i] = AccessorGenerator::For(field);
if (accessors_[i] == nullptr) {
ABSL_LOG(WARNING) << "unsupported field: " << field.desc().full_name();
}
}
}
void MessageGenerator::GenerateRs(Context<Descriptor> msg) {
msg.Emit(
{
{"Msg", msg.desc().name()},
{"pkg_Msg", GetUnderscoreDelimitedFullName(msg)},
{"Msg.fields", [&] { MessageStructFields(msg); }},
{"Msg::new", [&] { MessageNew(msg); }},
{"Msg::serialize", [&] { MessageSerialize(msg); }},
{"Msg::deserialize", [&] { MessageDeserialize(msg); }},
{"Msg_externs", [&] { MessageExterns(msg); }},
{"accessor_fns",
[&] {
for (int i = 0; i < msg.desc().field_count(); ++i) {
auto& gen = accessors_[i];
auto field = msg.WithDesc(*msg.desc().field(i));
msg.Emit({{"comment", FieldInfoComment(field)}}, R"rs(
// $comment$
)rs");
if (gen == nullptr) {
msg.Emit({{"field", field.desc().full_name()}}, R"rs(
// Unsupported! :(
)rs");
msg.printer().PrintRaw("\n");
continue;
}
gen->GenerateMsgImpl(field);
msg.printer().PrintRaw("\n");
}
}},
{"accessor_externs",
[&] {
for (int i = 0; i < msg.desc().field_count(); ++i) {
auto& gen = accessors_[i];
if (gen == nullptr) continue;
gen->GenerateExternC(msg.WithDesc(*msg.desc().field(i)));
msg.printer().PrintRaw("\n");
}
}},
},
R"rs(
#[allow(non_camel_case_types)]
pub struct $Msg$ {
$Msg.fields$
}
impl $Msg$ {
pub fn new() -> Self {
$Msg::new$
}
pub fn serialize(&self) -> $pb$::SerializedData {
$Msg::serialize$
}
pub fn deserialize(&mut self, data: &[u8]) -> Result<(), $pb$::ParseError> {
$Msg::deserialize$
}
$accessor_fns$
} // impl $Msg$
extern "C" {
$Msg_externs$
$accessor_externs$
} // extern "C" for $Msg$
)rs");
if (msg.is_cpp()) {
msg.printer().PrintRaw("\n");
msg.Emit({{"Msg", msg.desc().name()}}, R"rs(
impl $Msg$ {
pub fn __unstable_cpp_repr_grant_permission_to_break(&mut self) -> $NonNull$<u8> {
self.msg
}
}
)rs");
}
}
// Generates code for a particular message in `.pb.thunk.cc`.
void MessageGenerator::GenerateThunkCc(Context<Descriptor> msg) {
ABSL_CHECK(msg.is_cpp());
msg.Emit(
{
{"abi", "\"C\""}, // Workaround for syntax highlight bug in VSCode.
{"Msg", msg.desc().name()},
{"pkg_Msg", GetUnderscoreDelimitedFullName(msg)},
{"namespace", cpp::Namespace(&msg.desc())},
{"accessor_thunks",
[&] {
for (int i = 0; i < msg.desc().field_count(); ++i) {
auto& gen = accessors_[i];
if (gen == nullptr) continue;
gen->GenerateThunkCc(msg.WithDesc(*msg.desc().field(i)));
}
}},
},
R"cc(
extern $abi$ {
void* __rust_proto_thunk__$pkg_Msg$__new() {
return new $namespace$::$Msg$();
}
google::protobuf::rust_internal::SerializedData
__rust_proto_thunk__$pkg_Msg$__serialize($namespace$::$Msg$ * msg) {
return google::protobuf::rust_internal::SerializeMsg(msg);
}
bool __rust_proto_thunk__$pkg_Msg$__deserialize(
$namespace$::$Msg$ * msg,
google::protobuf::rust_internal::SerializedData data) {
return msg->ParseFromArray(data.data, data.len);
}
$accessor_thunks$
} // extern $abi$
)cc");
}
} // namespace rust
} // namespace compiler
} // namespace protobuf
} // namespace google

@ -0,0 +1,65 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google Inc. 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 Inc. 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_MESSAGE_H__
#define GOOGLE_PROTOBUF_COMPILER_RUST_MESSAGE_H__
#include <memory>
#include <vector>
#include "google/protobuf/compiler/rust/accessors/accessors.h"
#include "google/protobuf/compiler/rust/context.h"
#include "google/protobuf/descriptor.h"
namespace google {
namespace protobuf {
namespace compiler {
namespace rust {
class MessageGenerator final {
public:
explicit MessageGenerator(Context<Descriptor> msg);
// Generates code for a particular message in `.pb.rs`.
void GenerateRs(Context<Descriptor> msg);
// Generates code for a particular message in `.pb.thunk.cc`.
void GenerateThunkCc(Context<Descriptor> msg);
private:
std::vector<std::unique_ptr<AccessorGenerator>> accessors_;
};
} // namespace rust
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_RUST_MESSAGE_H__

@ -34,7 +34,6 @@
#include "absl/log/absl_log.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_join.h"
#include "absl/strings/str_replace.h"
#include "absl/strings/string_view.h"
#include "absl/strings/substitute.h"
@ -83,10 +82,17 @@ std::string GetUnderscoreDelimitedFullName(Context<Descriptor> msg) {
std::string GetAccessorThunkName(Context<FieldDescriptor> field,
absl::string_view op) {
std::string thunk = "__rust_proto_thunk__";
absl::StrAppend(&thunk, GetUnderscoreDelimitedFullName(
field.WithDesc(field.desc().containing_type())));
absl::SubstituteAndAppend(&thunk, "_$0_$1", op, field.desc().name());
absl::string_view prefix = field.is_cpp() ? "__rust_proto_thunk__" : "";
std::string thunk =
absl::StrCat(prefix, GetUnderscoreDelimitedFullName(
field.WithDesc(field.desc().containing_type())));
if (field.is_upb() && op.empty()) {
absl::SubstituteAndAppend(&thunk, "_$0", field.desc().name());
} else {
absl::SubstituteAndAppend(&thunk, "_$0_$1", op, field.desc().name());
}
return thunk;
}
@ -124,6 +130,23 @@ std::string RustModule(Context<Descriptor> msg) {
std::string GetCrateRelativeQualifiedPath(Context<Descriptor> msg) {
return absl::StrCat(RustModule(msg), "::", msg.desc().name());
}
std::string FieldInfoComment(Context<FieldDescriptor> field) {
absl::string_view label =
field.desc().is_repeated() ? "repeated" : "optional";
std::string comment =
absl::StrCat(field.desc().name(), ": ", label, " ",
FieldDescriptor::TypeName(field.desc().type()));
if (auto* m = field.desc().message_type()) {
absl::StrAppend(&comment, " ", m->full_name());
}
if (auto* m = field.desc().enum_type()) {
absl::StrAppend(&comment, " ", m->full_name());
}
return comment;
}
} // namespace rust
} // namespace compiler
} // namespace protobuf

@ -57,6 +57,8 @@ bool IsSupportedFieldType(Context<FieldDescriptor> field);
absl::string_view PrimitiveRsTypeName(Context<FieldDescriptor> field);
std::string FieldInfoComment(Context<FieldDescriptor> field);
std::string RustModule(Context<Descriptor> msg);
std::string GetCrateRelativeQualifiedPath(Context<Descriptor> msg);

Loading…
Cancel
Save