protobuf/src/google/protobuf/compiler/rust/naming.cc

218 lines
6.7 KiB
C++

// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include "google/protobuf/compiler/rust/naming.h"
#include <string>
#include "absl/log/absl_log.h"
#include "absl/strings/match.h"
#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"
#include "google/protobuf/descriptor.h"
namespace google {
namespace protobuf {
namespace compiler {
namespace rust {
namespace {
std::string GetUnderscoreDelimitedFullName(Context<Descriptor> msg) {
std::string result = msg.desc().full_name();
absl::StrReplaceAll({{".", "_"}}, &result);
return result;
}
} // namespace
std::string GetCrateName(Context<FileDescriptor> dep) {
absl::string_view path = dep.desc().name();
auto basename = path.substr(path.rfind('/') + 1);
return absl::StrReplaceAll(basename, {{".", "_"}, {"-", "_"}});
}
std::string GetRsFile(Context<FileDescriptor> file) {
auto basename = StripProto(file.desc().name());
switch (auto k = file.opts().kernel) {
case Kernel::kUpb:
return absl::StrCat(basename, ".u.pb.rs");
case Kernel::kCpp:
return absl::StrCat(basename, ".c.pb.rs");
default:
ABSL_LOG(FATAL) << "Unknown kernel type: " << static_cast<int>(k);
return "";
}
}
std::string GetThunkCcFile(Context<FileDescriptor> file) {
auto basename = StripProto(file.desc().name());
return absl::StrCat(basename, ".pb.thunks.cc");
}
std::string GetHeaderFile(Context<FileDescriptor> file) {
auto basename = StripProto(file.desc().name());
return absl::StrCat(basename, ".proto.h");
}
namespace {
template <typename T>
std::string FieldPrefix(Context<T> field) {
// 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_prefix =
absl::StrCat(prefix, GetUnderscoreDelimitedFullName(
field.WithDesc(field.desc().containing_type())));
return thunk_prefix;
}
template <typename T>
std::string Thunk(Context<T> field, absl::string_view op) {
std::string thunk = FieldPrefix(field);
absl::string_view format;
if (field.is_upb() && op == "get") {
// upb getter is simply the field name (no "get" in the name).
format = "_$1";
} else if (field.is_upb() && (op == "case")) {
// some upb functions are in the order x_op compared to has/set/clear which
// are in the other order e.g. op_x.
format = "_$1_$0";
} else {
format = "_$0_$1";
}
absl::SubstituteAndAppend(&thunk, format, op, field.desc().name());
return thunk;
}
std::string ThunkRepeated(Context<FieldDescriptor> field,
absl::string_view op) {
if (!field.is_upb()) {
return Thunk<FieldDescriptor>(field, op);
}
std::string thunk = absl::StrCat("_", FieldPrefix(field));
absl::string_view format;
if (op == "get") {
format = "_$1_upb_array";
} else if (op == "get_mut") {
format = "_$1_mutable_upb_array";
} else {
return Thunk<FieldDescriptor>(field, op);
}
absl::SubstituteAndAppend(&thunk, format, op, field.desc().name());
return thunk;
}
} // namespace
std::string Thunk(Context<FieldDescriptor> field, absl::string_view op) {
if (field.desc().is_repeated()) {
return ThunkRepeated(field, op);
}
return Thunk<FieldDescriptor>(field, op);
}
std::string Thunk(Context<OneofDescriptor> field, absl::string_view op) {
return Thunk<OneofDescriptor>(field, op);
}
std::string Thunk(Context<Descriptor> msg, absl::string_view op) {
absl::string_view prefix = msg.is_cpp() ? "__rust_proto_thunk__" : "";
return absl::StrCat(prefix, GetUnderscoreDelimitedFullName(msg), "_", op);
}
std::string PrimitiveRsTypeName(const FieldDescriptor& desc) {
switch (desc.type()) {
case FieldDescriptor::TYPE_BOOL:
return "bool";
case FieldDescriptor::TYPE_INT32:
case FieldDescriptor::TYPE_SINT32:
case FieldDescriptor::TYPE_SFIXED32:
return "i32";
case FieldDescriptor::TYPE_INT64:
case FieldDescriptor::TYPE_SINT64:
case FieldDescriptor::TYPE_SFIXED64:
return "i64";
case FieldDescriptor::TYPE_FIXED32:
case FieldDescriptor::TYPE_UINT32:
return "u32";
case FieldDescriptor::TYPE_FIXED64:
case FieldDescriptor::TYPE_UINT64:
return "u64";
case FieldDescriptor::TYPE_FLOAT:
return "f32";
case FieldDescriptor::TYPE_DOUBLE:
return "f64";
case FieldDescriptor::TYPE_BYTES:
return "[u8]";
case FieldDescriptor::TYPE_STRING:
return "::__pb::ProtoStr";
default:
break;
}
ABSL_LOG(FATAL) << "Unsupported field type: " << desc.type_name();
return "";
}
std::string RustModule(Context<Descriptor> msg) {
absl::string_view package = msg.desc().file()->package();
if (package.empty()) return "";
return absl::StrCat("", absl::StrReplaceAll(package, {{".", "::"}}));
}
std::string RustInternalModuleName(Context<FileDescriptor> file) {
// TODO: Introduce a more robust mangling here to avoid conflicts
// between `foo/bar/baz.proto` and `foo_bar/baz.proto`.
return absl::StrReplaceAll(StripProto(file.desc().name()), {{"/", "_"}});
}
std::string GetCrateRelativeQualifiedPath(Context<Descriptor> msg) {
std::string name = msg.desc().full_name();
if (msg.desc().file()->package().empty()) {
return name;
}
// when computing the relative path, we don't want the package name, so we
// strip that out
name =
std::string(absl::StripPrefix(name, msg.desc().file()->package() + "."));
// proto nesting is marked with periods in .proto files -- this gets
// translated to delimiting via _:: in terra rust
absl::StrReplaceAll({{".", "_::"}}, &name);
return absl::StrCat(RustModule(msg), "::", 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
} // namespace google