diff --git a/rust/codegen_traits.rs b/rust/codegen_traits.rs index f23e6043e79dc..ec33b7272c4f3 100644 --- a/rust/codegen_traits.rs +++ b/rust/codegen_traits.rs @@ -31,6 +31,18 @@ impl> MessageTypeHelper MessageType for T where T: EntityType + MessageTypeHelper {} +/// Provides compile-time access to a generated message's protobuf full name. +/// +/// This exists for generic code that needs to name message types without going +/// through runtime reflection or descriptor APIs. +/// +/// Bring `protobuf::MessageFullName` into scope to use `MyMessage::FULL_NAME`. +/// Without the trait in scope, use `::FULL_NAME`. +pub trait MessageFullName: MessageType { + /// The protobuf full name for the generated message type. + const FULL_NAME: &'static str; +} + /// A trait that all generated owned message types implement. pub trait Message: SealedInternal + EntityType diff --git a/rust/shared.rs b/rust/shared.rs index bb5f78e32d4c8..cc9e81de1c124 100644 --- a/rust/shared.rs +++ b/rust/shared.rs @@ -22,7 +22,7 @@ pub use crate::codegen_traits::{ create::Parse, read::Serialize, write::{Clear, ClearAndParse, CopyFrom, MergeFrom, TakeFrom}, - Message, MessageMut, MessageType, MessageView, + Message, MessageFullName, MessageMut, MessageType, MessageView, }; pub use crate::cord::{ProtoBytesCow, ProtoStringCow}; pub use crate::extension::ExtensionId; diff --git a/rust/test/shared/BUILD b/rust/test/shared/BUILD index 2eb6b2c65401c..45c6e2cc249c0 100644 --- a/rust/test/shared/BUILD +++ b/rust/test/shared/BUILD @@ -115,6 +115,34 @@ rust_test( ], ) +rust_test( + name = "full_name_cpp_test", + srcs = ["full_name_test.rs"], + aliases = { + "//rust:protobuf_cpp_export": "protobuf", + }, + rustc_flags = ["--cfg=bzl"], + deps = [ + "//rust:protobuf_cpp_export", + "//rust/test:unittest_cpp_rust_proto", + "@crate_index//:googletest", + ], +) + +rust_test( + name = "full_name_upb_test", + srcs = ["full_name_test.rs"], + aliases = { + "//rust:protobuf_upb_export": "protobuf", + }, + rustc_flags = ["--cfg=bzl"], + deps = [ + "//rust:protobuf_upb_export", + "//rust/test:unittest_upb_rust_proto", + "@crate_index//:googletest", + ], +) + rust_test( name = "enum_cpp_test", srcs = ["enum_test.rs"], diff --git a/rust/test/shared/full_name_test.rs b/rust/test/shared/full_name_test.rs new file mode 100644 index 0000000000000..a718dd0680981 --- /dev/null +++ b/rust/test/shared/full_name_test.rs @@ -0,0 +1,59 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2026 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 googletest::prelude::*; +use protobuf::MessageFullName; +use unittest_rust_proto::{test_all_types, NestedTestAllTypes, TestAllTypes}; + +#[gtest] +fn generated_messages_expose_full_name_via_concrete_syntax() { + assert_that!(TestAllTypes::FULL_NAME, eq("rust_unittest.TestAllTypes")); + assert_that!( + NestedTestAllTypes::FULL_NAME, + eq("rust_unittest.NestedTestAllTypes") + ); + assert_that!( + test_all_types::NestedMessage::FULL_NAME, + eq("rust_unittest.TestAllTypes.NestedMessage") + ); +} + +fn generic_message_full_name() -> &'static str { + T::FULL_NAME +} + +#[gtest] +fn generated_messages_expose_full_name_via_generic_trait() { + assert_that!( + generic_message_full_name::(), + eq("rust_unittest.TestAllTypes") + ); + assert_that!( + generic_message_full_name::(), + eq("rust_unittest.NestedTestAllTypes") + ); + assert_that!( + generic_message_full_name::(), + eq("rust_unittest.TestAllTypes.NestedMessage") + ); +} + +#[gtest] +fn generated_messages_expose_full_name_via_ufcs() { + assert_that!( + ::FULL_NAME, + eq(TestAllTypes::FULL_NAME) + ); + assert_that!( + ::FULL_NAME, + eq(NestedTestAllTypes::FULL_NAME) + ); + assert_that!( + ::FULL_NAME, + eq(test_all_types::NestedMessage::FULL_NAME) + ); +} diff --git a/src/google/protobuf/compiler/rust/message.cc b/src/google/protobuf/compiler/rust/message.cc index e00c97388f402..86db8a24d8e9d 100644 --- a/src/google/protobuf/compiler/rust/message.cc +++ b/src/google/protobuf/compiler/rust/message.cc @@ -514,6 +514,7 @@ void GenerateRs(Context& ctx, const Descriptor& msg, const upb::DefPool& pool) { CppGeneratedMessageTraitImpls(ctx, msg); } }}, + {"full_name", msg.full_name()}, {"type_conversions_impl", [&] { TypeConversions(ctx, msg); }}, {"unwrap_upb", [&] { @@ -763,6 +764,10 @@ void GenerateRs(Context& ctx, const Descriptor& msg, const upb::DefPool& pool) { } } + impl $pb$::MessageFullName for $Msg$ { + const FULL_NAME: &'static str = "$full_name$"; + } + impl $pb$::AsView for $Msg$ { type Proxied = Self; fn as_view(&self) -> $Msg$View<'_> {