Expose serialized descriptors in Rust-upb
The goal is to make it possible for gRPC to implement the reflection service
[here](https://github.com/grpc/grpc-proto/blob/23f5b568eefcb876e6ebc3b01725f1f20cff999e/grpc/reflection/v1/reflection.proto).
In C++ we have a global descriptor pool, but that pattern is not an option here
because we need to be able to handle the possibility of multiple proto files
with the same name linked into a single binary.
PiperOrigin-RevId: 811896543
diff --git a/rust/defs.bzl b/rust/defs.bzl
index b787d10..9d2dcd5 100644
--- a/rust/defs.bzl
+++ b/rust/defs.bzl
@@ -52,14 +52,14 @@
rust_upb_proto_library(
name = name + "_upb_rust_proto",
deps = deps,
- visibility = ["//visibility:private"],
+ visibility = ["//rust/test:__subpackages__"],
**args
)
rust_cc_proto_library(
name = name + "_cpp_rust_proto",
deps = deps,
- visibility = ["//visibility:private"],
+ visibility = ["//rust/test:__subpackages__"],
**args
)
diff --git a/rust/test/upb/BUILD b/rust/test/upb/BUILD
index 658f717..c8820cd 100644
--- a/rust/test/upb/BUILD
+++ b/rust/test/upb/BUILD
@@ -75,3 +75,17 @@
"@crate_index//:googletest",
],
)
+
+rust_test(
+ name = "generated_descriptors_test",
+ srcs = ["generated_descriptors_test.rs"],
+ aliases = {
+ "//rust:protobuf_upb_export": "protobuf",
+ },
+ deps = [
+ "//net/proto2/proto:descriptor_upb_rust_proto",
+ "//rust:protobuf_upb_export",
+ "//rust/test:unittest_upb_rust_proto",
+ "@crate_index//:googletest",
+ ],
+)
diff --git a/rust/test/upb/generated_descriptors_test.rs b/rust/test/upb/generated_descriptors_test.rs
new file mode 100644
index 0000000..76a4951
--- /dev/null
+++ b/rust/test/upb/generated_descriptors_test.rs
@@ -0,0 +1,39 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2025 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
+
+use descriptor_rust_proto::FileDescriptorProto;
+use googletest::prelude::*;
+use protobuf::prelude::*;
+
+#[gtest]
+fn test_generated_descriptors() {
+ let descriptor = FileDescriptorProto::parse(
+ unittest_rust_proto::__unstable::THIRD_PARTY_PROTOBUF_RUST_TEST_UNITTEST_DESCRIPTOR_INFO
+ .descriptor,
+ )
+ .unwrap();
+ expect_that!(descriptor.name(), eq("third_party/protobuf/rust/test/unittest.proto"));
+ assert_that!(descriptor.dependency().len(), eq(1));
+ expect_that!(
+ descriptor.dependency().get(0).unwrap(),
+ eq("third_party/protobuf/rust/test/unittest_import.proto")
+ );
+
+ assert_that!(
+ unittest_rust_proto::__unstable::THIRD_PARTY_PROTOBUF_RUST_TEST_UNITTEST_DESCRIPTOR_INFO
+ .deps
+ .len(),
+ eq(1)
+ );
+ let dep = FileDescriptorProto::parse(
+ unittest_rust_proto::__unstable::THIRD_PARTY_PROTOBUF_RUST_TEST_UNITTEST_DESCRIPTOR_INFO
+ .deps[0]
+ .descriptor,
+ )
+ .unwrap();
+ expect_that!(dep.name(), eq("third_party/protobuf/rust/test/unittest_import.proto"));
+}
diff --git a/rust/upb.rs b/rust/upb.rs
index 037f93f..21a322b 100644
--- a/rust/upb.rs
+++ b/rust/upb.rs
@@ -1350,3 +1350,14 @@
parent.ptr.set_map_at_index(index, inner.as_raw());
}
}
+
+pub mod __unstable {
+ // Stores a serialized FileDescriptorProto, along with references to its dependencies.
+ pub struct DescriptorInfo {
+ // The serialized FileDescriptorProto.
+ pub descriptor: &'static [u8],
+ // A reference to the DescriptorInfo associated with each .proto file that the current one
+ // imports.
+ pub deps: &'static [&'static DescriptorInfo],
+ }
+}
diff --git a/src/google/protobuf/compiler/rust/generator.cc b/src/google/protobuf/compiler/rust/generator.cc
index 1ad871a..9115282 100644
--- a/src/google/protobuf/compiler/rust/generator.cc
+++ b/src/google/protobuf/compiler/rust/generator.cc
@@ -18,6 +18,7 @@
#include "absl/memory/memory.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
+#include "absl/strings/escaping.h"
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_replace.h"
@@ -136,6 +137,40 @@
pub use internal_do_not_use_$mod_name$::*;
)rs");
}
+
+ auto v = ctx.printer().WithVars({
+ {"pbu", "::protobuf::__internal::runtime::__unstable"},
+ });
+ if (ctx.is_upb() && !ctx.opts().strip_nonfunctional_codegen) {
+ ctx.Emit(R"rs(
+ pub mod __unstable {
+ )rs");
+ for (const FileDescriptor* file : files) {
+ FileDescriptorProto descriptor_proto;
+ file->CopyTo(&descriptor_proto);
+ ctx.Emit({{"name", DescriptorInfoName(*file)},
+ {"serialized_descriptor",
+ absl::CHexEscape(descriptor_proto.SerializeAsString())},
+ {"deps",
+ [&] {
+ for (int i = 0; i < file->dependency_count(); ++i) {
+ const FileDescriptor& dep = *file->dependency(i);
+ ctx.Emit({{"crate", GetCrateName(ctx, dep)},
+ {"dep_name", DescriptorInfoName(dep)}},
+ "&$crate$::__unstable::$dep_name$,\n");
+ }
+ }}},
+ R"rs(
+ pub static $name$: $pbu$::DescriptorInfo = $pbu$::DescriptorInfo {
+ descriptor: b"$serialized_descriptor$",
+ deps: &[
+ $deps$
+ ],
+ };
+ )rs");
+ }
+ ctx.Emit("}\n");
+ }
}
} // namespace
diff --git a/src/google/protobuf/compiler/rust/naming.cc b/src/google/protobuf/compiler/rust/naming.cc
index bcb93cb..7a6a8bf 100644
--- a/src/google/protobuf/compiler/rust/naming.cc
+++ b/src/google/protobuf/compiler/rust/naming.cc
@@ -439,6 +439,13 @@
return name;
}
+std::string DescriptorInfoName(const FileDescriptor& file) {
+ std::string name =
+ absl::StrReplaceAll(StripProto(file.name()), {{"/", "_"}, {"-", "_"}});
+ absl::AsciiStrToUpper(&name);
+ return absl::StrCat(name, "_DESCRIPTOR_INFO");
+}
+
} // 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 eaae5fa..e440e4f 100644
--- a/src/google/protobuf/compiler/rust/naming.h
+++ b/src/google/protobuf/compiler/rust/naming.h
@@ -148,6 +148,9 @@
std::string EnumValueRsName(const MultiCasePrefixStripper& stripper,
absl::string_view value_name);
+// Returns the name of the generated DescriptorInfo object for the given file.
+std::string DescriptorInfoName(const FileDescriptor& file);
+
// Describes the names and conversions for a supported map key type.
struct MapKeyType {
// Identifier used in thunk name.