From 3177e64693b4fd218831ff05a5be19117f5b1b30 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Sat, 9 May 2026 17:36:47 -0400 Subject: [PATCH] feat(buffa): add MessageFullName trait Signed-off-by: Yordis Prieto --- buffa-codegen/src/impl_message.rs | 4 + .../google.protobuf.compiler.plugin.rs | 12 +++ .../generated/google.protobuf.descriptor.rs | 102 ++++++++++++++++++ buffa-test/src/tests/basic.rs | 9 +- .../src/generated/google.protobuf.any.rs | 3 + .../src/generated/google.protobuf.duration.rs | 3 + .../src/generated/google.protobuf.empty.rs | 3 + .../generated/google.protobuf.field_mask.rs | 3 + .../src/generated/google.protobuf.struct.rs | 9 ++ .../generated/google.protobuf.timestamp.rs | 3 + .../src/generated/google.protobuf.wrappers.rs | 27 +++++ buffa-types/src/lib.rs | 21 ++++ buffa/src/extension.rs | 12 +++ buffa/src/lib.rs | 3 +- buffa/src/message.rs | 34 ++++++ docs/guide.md | 7 ++ .../src/gen/buffa.examples.context.v1.mod.rs | 1 + .../src/gen/buffa.examples.log.v1.mod.rs | 1 + examples/logging/src/gen/mod.rs | 14 +-- 19 files changed, 262 insertions(+), 9 deletions(-) diff --git a/buffa-codegen/src/impl_message.rs b/buffa-codegen/src/impl_message.rs index 8ce3f50..8f46da7 100644 --- a/buffa-codegen/src/impl_message.rs +++ b/buffa-codegen/src/impl_message.rs @@ -552,6 +552,10 @@ pub fn generate_message_impl( } } + impl ::buffa::MessageFullName for #name_ident { + const FULL_NAME: &'static str = #proto_fqn; + } + impl ::buffa::Message for #name_ident { /// Returns the total encoded size in bytes. /// diff --git a/buffa-descriptor/src/generated/google.protobuf.compiler.plugin.rs b/buffa-descriptor/src/generated/google.protobuf.compiler.plugin.rs index 8fc20ca..1d220ee 100644 --- a/buffa-descriptor/src/generated/google.protobuf.compiler.plugin.rs +++ b/buffa-descriptor/src/generated/google.protobuf.compiler.plugin.rs @@ -41,6 +41,9 @@ impl ::buffa::DefaultInstance for Version { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for Version { + const FULL_NAME: &'static str = "google.protobuf.compiler.Version"; +} impl ::buffa::Message for Version { /// Returns the total encoded size in bytes. /// @@ -255,6 +258,9 @@ impl ::buffa::DefaultInstance for CodeGeneratorRequest { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for CodeGeneratorRequest { + const FULL_NAME: &'static str = "google.protobuf.compiler.CodeGeneratorRequest"; +} impl ::buffa::Message for CodeGeneratorRequest { /// Returns the total encoded size in bytes. /// @@ -512,6 +518,9 @@ impl ::buffa::DefaultInstance for CodeGeneratorResponse { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for CodeGeneratorResponse { + const FULL_NAME: &'static str = "google.protobuf.compiler.CodeGeneratorResponse"; +} impl ::buffa::Message for CodeGeneratorResponse { /// Returns the total encoded size in bytes. /// @@ -832,6 +841,9 @@ pub mod code_generator_response { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } + impl ::buffa::MessageFullName for File { + const FULL_NAME: &'static str = "google.protobuf.compiler.CodeGeneratorResponse.File"; + } impl ::buffa::Message for File { /// Returns the total encoded size in bytes. /// diff --git a/buffa-descriptor/src/generated/google.protobuf.descriptor.rs b/buffa-descriptor/src/generated/google.protobuf.descriptor.rs index b86be0d..d1f800f 100644 --- a/buffa-descriptor/src/generated/google.protobuf.descriptor.rs +++ b/buffa-descriptor/src/generated/google.protobuf.descriptor.rs @@ -200,6 +200,9 @@ impl ::buffa::DefaultInstance for FileDescriptorSet { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for FileDescriptorSet { + const FULL_NAME: &'static str = "google.protobuf.FileDescriptorSet"; +} impl ::buffa::Message for FileDescriptorSet { /// Returns the total encoded size in bytes. /// @@ -385,6 +388,9 @@ impl ::buffa::DefaultInstance for FileDescriptorProto { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for FileDescriptorProto { + const FULL_NAME: &'static str = "google.protobuf.FileDescriptorProto"; +} impl ::buffa::Message for FileDescriptorProto { /// Returns the total encoded size in bytes. /// @@ -913,6 +919,9 @@ impl ::buffa::DefaultInstance for DescriptorProto { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for DescriptorProto { + const FULL_NAME: &'static str = "google.protobuf.DescriptorProto"; +} impl ::buffa::Message for DescriptorProto { /// Returns the total encoded size in bytes. /// @@ -1326,6 +1335,9 @@ pub mod descriptor_proto { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } + impl ::buffa::MessageFullName for ExtensionRange { + const FULL_NAME: &'static str = "google.protobuf.DescriptorProto.ExtensionRange"; + } impl ::buffa::Message for ExtensionRange { /// Returns the total encoded size in bytes. /// @@ -1491,6 +1503,9 @@ pub mod descriptor_proto { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } + impl ::buffa::MessageFullName for ReservedRange { + const FULL_NAME: &'static str = "google.protobuf.DescriptorProto.ReservedRange"; + } impl ::buffa::Message for ReservedRange { /// Returns the total encoded size in bytes. /// @@ -1636,6 +1651,9 @@ impl ::buffa::DefaultInstance for ExtensionRangeOptions { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for ExtensionRangeOptions { + const FULL_NAME: &'static str = "google.protobuf.ExtensionRangeOptions"; +} impl ::buffa::Message for ExtensionRangeOptions { /// Returns the total encoded size in bytes. /// @@ -1911,6 +1929,9 @@ pub mod extension_range_options { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } + impl ::buffa::MessageFullName for Declaration { + const FULL_NAME: &'static str = "google.protobuf.ExtensionRangeOptions.Declaration"; + } impl ::buffa::Message for Declaration { /// Returns the total encoded size in bytes. /// @@ -2188,6 +2209,9 @@ impl ::buffa::DefaultInstance for FieldDescriptorProto { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for FieldDescriptorProto { + const FULL_NAME: &'static str = "google.protobuf.FieldDescriptorProto"; +} impl ::buffa::Message for FieldDescriptorProto { /// Returns the total encoded size in bytes. /// @@ -2749,6 +2773,9 @@ impl ::buffa::DefaultInstance for OneofDescriptorProto { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for OneofDescriptorProto { + const FULL_NAME: &'static str = "google.protobuf.OneofDescriptorProto"; +} impl ::buffa::Message for OneofDescriptorProto { /// Returns the total encoded size in bytes. /// @@ -2914,6 +2941,9 @@ impl ::buffa::DefaultInstance for EnumDescriptorProto { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for EnumDescriptorProto { + const FULL_NAME: &'static str = "google.protobuf.EnumDescriptorProto"; +} impl ::buffa::Message for EnumDescriptorProto { /// Returns the total encoded size in bytes. /// @@ -3180,6 +3210,9 @@ pub mod enum_descriptor_proto { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } + impl ::buffa::MessageFullName for EnumReservedRange { + const FULL_NAME: &'static str = "google.protobuf.EnumDescriptorProto.EnumReservedRange"; + } impl ::buffa::Message for EnumReservedRange { /// Returns the total encoded size in bytes. /// @@ -3311,6 +3344,9 @@ impl ::buffa::DefaultInstance for EnumValueDescriptorProto { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for EnumValueDescriptorProto { + const FULL_NAME: &'static str = "google.protobuf.EnumValueDescriptorProto"; +} impl ::buffa::Message for EnumValueDescriptorProto { /// Returns the total encoded size in bytes. /// @@ -3477,6 +3513,9 @@ impl ::buffa::DefaultInstance for ServiceDescriptorProto { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for ServiceDescriptorProto { + const FULL_NAME: &'static str = "google.protobuf.ServiceDescriptorProto"; +} impl ::buffa::Message for ServiceDescriptorProto { /// Returns the total encoded size in bytes. /// @@ -3668,6 +3707,9 @@ impl ::buffa::DefaultInstance for MethodDescriptorProto { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for MethodDescriptorProto { + const FULL_NAME: &'static str = "google.protobuf.MethodDescriptorProto"; +} impl ::buffa::Message for MethodDescriptorProto { /// Returns the total encoded size in bytes. /// @@ -4082,6 +4124,9 @@ impl ::buffa::DefaultInstance for FileOptions { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for FileOptions { + const FULL_NAME: &'static str = "google.protobuf.FileOptions"; +} impl ::buffa::Message for FileOptions { /// Returns the total encoded size in bytes. /// @@ -4844,6 +4889,9 @@ impl ::buffa::DefaultInstance for MessageOptions { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for MessageOptions { + const FULL_NAME: &'static str = "google.protobuf.MessageOptions"; +} impl ::buffa::Message for MessageOptions { /// Returns the total encoded size in bytes. /// @@ -5204,6 +5252,9 @@ impl ::buffa::DefaultInstance for FieldOptions { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for FieldOptions { + const FULL_NAME: &'static str = "google.protobuf.FieldOptions"; +} impl ::buffa::Message for FieldOptions { /// Returns the total encoded size in bytes. /// @@ -5944,6 +5995,9 @@ pub mod field_options { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } + impl ::buffa::MessageFullName for EditionDefault { + const FULL_NAME: &'static str = "google.protobuf.FieldOptions.EditionDefault"; + } impl ::buffa::Message for EditionDefault { /// Returns the total encoded size in bytes. /// @@ -6106,6 +6160,9 @@ pub mod field_options { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } + impl ::buffa::MessageFullName for FeatureSupport { + const FULL_NAME: &'static str = "google.protobuf.FieldOptions.FeatureSupport"; + } impl ::buffa::Message for FeatureSupport { /// Returns the total encoded size in bytes. /// @@ -6315,6 +6372,9 @@ impl ::buffa::DefaultInstance for OneofOptions { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for OneofOptions { + const FULL_NAME: &'static str = "google.protobuf.OneofOptions"; +} impl ::buffa::Message for OneofOptions { /// Returns the total encoded size in bytes. /// @@ -6495,6 +6555,9 @@ impl ::buffa::DefaultInstance for EnumOptions { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for EnumOptions { + const FULL_NAME: &'static str = "google.protobuf.EnumOptions"; +} impl ::buffa::Message for EnumOptions { /// Returns the total encoded size in bytes. /// @@ -6731,6 +6794,9 @@ impl ::buffa::DefaultInstance for EnumValueOptions { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for EnumValueOptions { + const FULL_NAME: &'static str = "google.protobuf.EnumValueOptions"; +} impl ::buffa::Message for EnumValueOptions { /// Returns the total encoded size in bytes. /// @@ -6971,6 +7037,9 @@ impl ::buffa::DefaultInstance for ServiceOptions { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for ServiceOptions { + const FULL_NAME: &'static str = "google.protobuf.ServiceOptions"; +} impl ::buffa::Message for ServiceOptions { /// Returns the total encoded size in bytes. /// @@ -7161,6 +7230,9 @@ impl ::buffa::DefaultInstance for MethodOptions { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for MethodOptions { + const FULL_NAME: &'static str = "google.protobuf.MethodOptions"; +} impl ::buffa::Message for MethodOptions { /// Returns the total encoded size in bytes. /// @@ -7436,6 +7508,9 @@ impl ::buffa::DefaultInstance for UninterpretedOption { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for UninterpretedOption { + const FULL_NAME: &'static str = "google.protobuf.UninterpretedOption"; +} impl ::buffa::Message for UninterpretedOption { /// Returns the total encoded size in bytes. /// @@ -7700,6 +7775,9 @@ pub mod uninterpreted_option { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } + impl ::buffa::MessageFullName for NamePart { + const FULL_NAME: &'static str = "google.protobuf.UninterpretedOption.NamePart"; + } impl ::buffa::Message for NamePart { /// Returns the total encoded size in bytes. /// @@ -7849,6 +7927,9 @@ impl ::buffa::DefaultInstance for FeatureSet { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for FeatureSet { + const FULL_NAME: &'static str = "google.protobuf.FeatureSet"; +} impl ::buffa::Message for FeatureSet { /// Returns the total encoded size in bytes. /// @@ -8499,6 +8580,9 @@ pub mod feature_set { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } + impl ::buffa::MessageFullName for VisibilityFeature { + const FULL_NAME: &'static str = "google.protobuf.FeatureSet.VisibilityFeature"; + } impl ::buffa::Message for VisibilityFeature { /// Returns the total encoded size in bytes. /// @@ -8678,6 +8762,9 @@ impl ::buffa::DefaultInstance for FeatureSetDefaults { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for FeatureSetDefaults { + const FULL_NAME: &'static str = "google.protobuf.FeatureSetDefaults"; +} impl ::buffa::Message for FeatureSetDefaults { /// Returns the total encoded size in bytes. /// @@ -8866,6 +8953,9 @@ pub mod feature_set_defaults { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } + impl ::buffa::MessageFullName for FeatureSetEditionDefault { + const FULL_NAME: &'static str = "google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault"; + } impl ::buffa::Message for FeatureSetEditionDefault { /// Returns the total encoded size in bytes. /// @@ -9091,6 +9181,9 @@ impl ::buffa::DefaultInstance for SourceCodeInfo { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for SourceCodeInfo { + const FULL_NAME: &'static str = "google.protobuf.SourceCodeInfo"; +} impl ::buffa::Message for SourceCodeInfo { /// Returns the total encoded size in bytes. /// @@ -9303,6 +9396,9 @@ pub mod source_code_info { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } + impl ::buffa::MessageFullName for Location { + const FULL_NAME: &'static str = "google.protobuf.SourceCodeInfo.Location"; + } impl ::buffa::Message for Location { /// Returns the total encoded size in bytes. /// @@ -9580,6 +9676,9 @@ impl ::buffa::DefaultInstance for GeneratedCodeInfo { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for GeneratedCodeInfo { + const FULL_NAME: &'static str = "google.protobuf.GeneratedCodeInfo"; +} impl ::buffa::Message for GeneratedCodeInfo { /// Returns the total encoded size in bytes. /// @@ -9720,6 +9819,9 @@ pub mod generated_code_info { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } + impl ::buffa::MessageFullName for Annotation { + const FULL_NAME: &'static str = "google.protobuf.GeneratedCodeInfo.Annotation"; + } impl ::buffa::Message for Annotation { /// Returns the total encoded size in bytes. /// diff --git a/buffa-test/src/tests/basic.rs b/buffa-test/src/tests/basic.rs index 9c91fdf..f0d97ac 100644 --- a/buffa-test/src/tests/basic.rs +++ b/buffa-test/src/tests/basic.rs @@ -3,7 +3,14 @@ use super::round_trip; use crate::basic::__buffa::oneof; use crate::basic::*; -use buffa::Message; +use buffa::{Message, MessageFullName}; + +#[test] +fn test_full_name_const_matches_proto_fqn() { + assert_eq!(Empty::FULL_NAME, "basic.Empty"); + assert_eq!(Person::FULL_NAME, "basic.Person"); + assert_eq!(AllScalars::FULL_NAME, "basic.AllScalars"); +} #[test] fn test_empty_message_encodes_to_zero_bytes() { diff --git a/buffa-types/src/generated/google.protobuf.any.rs b/buffa-types/src/generated/google.protobuf.any.rs index f9e6b9d..bc680a2 100644 --- a/buffa-types/src/generated/google.protobuf.any.rs +++ b/buffa-types/src/generated/google.protobuf.any.rs @@ -164,6 +164,9 @@ impl ::buffa::DefaultInstance for Any { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for Any { + const FULL_NAME: &'static str = "google.protobuf.Any"; +} impl ::buffa::Message for Any { /// Returns the total encoded size in bytes. /// diff --git a/buffa-types/src/generated/google.protobuf.duration.rs b/buffa-types/src/generated/google.protobuf.duration.rs index 793792b..4735845 100644 --- a/buffa-types/src/generated/google.protobuf.duration.rs +++ b/buffa-types/src/generated/google.protobuf.duration.rs @@ -107,6 +107,9 @@ impl ::buffa::DefaultInstance for Duration { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for Duration { + const FULL_NAME: &'static str = "google.protobuf.Duration"; +} impl ::buffa::Message for Duration { /// Returns the total encoded size in bytes. /// diff --git a/buffa-types/src/generated/google.protobuf.empty.rs b/buffa-types/src/generated/google.protobuf.empty.rs index af554f5..3767363 100644 --- a/buffa-types/src/generated/google.protobuf.empty.rs +++ b/buffa-types/src/generated/google.protobuf.empty.rs @@ -34,6 +34,9 @@ impl ::buffa::DefaultInstance for Empty { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for Empty { + const FULL_NAME: &'static str = "google.protobuf.Empty"; +} impl ::buffa::Message for Empty { /// Returns the total encoded size in bytes. /// diff --git a/buffa-types/src/generated/google.protobuf.field_mask.rs b/buffa-types/src/generated/google.protobuf.field_mask.rs index 062dd7c..5502a26 100644 --- a/buffa-types/src/generated/google.protobuf.field_mask.rs +++ b/buffa-types/src/generated/google.protobuf.field_mask.rs @@ -252,6 +252,9 @@ impl ::buffa::DefaultInstance for FieldMask { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for FieldMask { + const FULL_NAME: &'static str = "google.protobuf.FieldMask"; +} impl ::buffa::Message for FieldMask { /// Returns the total encoded size in bytes. /// diff --git a/buffa-types/src/generated/google.protobuf.struct.rs b/buffa-types/src/generated/google.protobuf.struct.rs index f4eaca3..0868257 100644 --- a/buffa-types/src/generated/google.protobuf.struct.rs +++ b/buffa-types/src/generated/google.protobuf.struct.rs @@ -78,6 +78,9 @@ impl ::buffa::DefaultInstance for Struct { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for Struct { + const FULL_NAME: &'static str = "google.protobuf.Struct"; +} impl ::buffa::Message for Struct { /// Returns the total encoded size in bytes. /// @@ -349,6 +352,9 @@ impl ::buffa::DefaultInstance for Value { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for Value { + const FULL_NAME: &'static str = "google.protobuf.Value"; +} impl ::buffa::Message for Value { /// Returns the total encoded size in bytes. /// @@ -760,6 +766,9 @@ impl ::buffa::DefaultInstance for ListValue { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for ListValue { + const FULL_NAME: &'static str = "google.protobuf.ListValue"; +} impl ::buffa::Message for ListValue { /// Returns the total encoded size in bytes. /// diff --git a/buffa-types/src/generated/google.protobuf.timestamp.rs b/buffa-types/src/generated/google.protobuf.timestamp.rs index db64a11..108043c 100644 --- a/buffa-types/src/generated/google.protobuf.timestamp.rs +++ b/buffa-types/src/generated/google.protobuf.timestamp.rs @@ -142,6 +142,9 @@ impl ::buffa::DefaultInstance for Timestamp { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for Timestamp { + const FULL_NAME: &'static str = "google.protobuf.Timestamp"; +} impl ::buffa::Message for Timestamp { /// Returns the total encoded size in bytes. /// diff --git a/buffa-types/src/generated/google.protobuf.wrappers.rs b/buffa-types/src/generated/google.protobuf.wrappers.rs index 6aceba3..e49e362 100644 --- a/buffa-types/src/generated/google.protobuf.wrappers.rs +++ b/buffa-types/src/generated/google.protobuf.wrappers.rs @@ -32,6 +32,9 @@ impl ::buffa::DefaultInstance for DoubleValue { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for DoubleValue { + const FULL_NAME: &'static str = "google.protobuf.DoubleValue"; +} impl ::buffa::Message for DoubleValue { /// Returns the total encoded size in bytes. /// @@ -171,6 +174,9 @@ impl ::buffa::DefaultInstance for FloatValue { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for FloatValue { + const FULL_NAME: &'static str = "google.protobuf.FloatValue"; +} impl ::buffa::Message for FloatValue { /// Returns the total encoded size in bytes. /// @@ -310,6 +316,9 @@ impl ::buffa::DefaultInstance for Int64Value { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for Int64Value { + const FULL_NAME: &'static str = "google.protobuf.Int64Value"; +} impl ::buffa::Message for Int64Value { /// Returns the total encoded size in bytes. /// @@ -449,6 +458,9 @@ impl ::buffa::DefaultInstance for UInt64Value { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for UInt64Value { + const FULL_NAME: &'static str = "google.protobuf.UInt64Value"; +} impl ::buffa::Message for UInt64Value { /// Returns the total encoded size in bytes. /// @@ -588,6 +600,9 @@ impl ::buffa::DefaultInstance for Int32Value { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for Int32Value { + const FULL_NAME: &'static str = "google.protobuf.Int32Value"; +} impl ::buffa::Message for Int32Value { /// Returns the total encoded size in bytes. /// @@ -727,6 +742,9 @@ impl ::buffa::DefaultInstance for UInt32Value { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for UInt32Value { + const FULL_NAME: &'static str = "google.protobuf.UInt32Value"; +} impl ::buffa::Message for UInt32Value { /// Returns the total encoded size in bytes. /// @@ -866,6 +884,9 @@ impl ::buffa::DefaultInstance for BoolValue { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for BoolValue { + const FULL_NAME: &'static str = "google.protobuf.BoolValue"; +} impl ::buffa::Message for BoolValue { /// Returns the total encoded size in bytes. /// @@ -1005,6 +1026,9 @@ impl ::buffa::DefaultInstance for StringValue { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for StringValue { + const FULL_NAME: &'static str = "google.protobuf.StringValue"; +} impl ::buffa::Message for StringValue { /// Returns the total encoded size in bytes. /// @@ -1147,6 +1171,9 @@ impl ::buffa::DefaultInstance for BytesValue { VALUE.get_or_init(|| ::buffa::alloc::boxed::Box::new(Self::default())) } } +impl ::buffa::MessageFullName for BytesValue { + const FULL_NAME: &'static str = "google.protobuf.BytesValue"; +} impl ::buffa::Message for BytesValue { /// Returns the total encoded size in bytes. /// diff --git a/buffa-types/src/lib.rs b/buffa-types/src/lib.rs index 3b63f04..ff472bf 100644 --- a/buffa-types/src/lib.rs +++ b/buffa-types/src/lib.rs @@ -87,3 +87,24 @@ pub use timestamp_ext::TimestampError; // Re-export the WKT registry function for `Any` JSON + text support. pub use any_ext::register_wkt_types; + +#[cfg(test)] +mod full_name_tests { + use super::google::protobuf::*; + use buffa::MessageFullName; + + // Regression test: the WKT FQNs are baked into Any type-URLs, JSON + // serialization, and the type registry. Codegen must keep emitting them + // verbatim — these strings are observable on the wire. + #[test] + fn well_known_types_full_names_match_proto() { + assert_eq!(Timestamp::FULL_NAME, "google.protobuf.Timestamp"); + assert_eq!(Duration::FULL_NAME, "google.protobuf.Duration"); + assert_eq!(Any::FULL_NAME, "google.protobuf.Any"); + assert_eq!(Empty::FULL_NAME, "google.protobuf.Empty"); + assert_eq!(FieldMask::FULL_NAME, "google.protobuf.FieldMask"); + assert_eq!(Struct::FULL_NAME, "google.protobuf.Struct"); + assert_eq!(Value::FULL_NAME, "google.protobuf.Value"); + assert_eq!(ListValue::FULL_NAME, "google.protobuf.ListValue"); + } +} diff --git a/buffa/src/extension.rs b/buffa/src/extension.rs index d6643c1..e433fcb 100644 --- a/buffa/src/extension.rs +++ b/buffa/src/extension.rs @@ -215,6 +215,18 @@ pub trait ExtensionSet { /// Checked against [`Extension::extendee`] on every `extension()`, /// `set_extension()`, and `clear_extension()` call. A mismatch panics: /// passing an extension for the wrong message is a bug in the caller. + /// + /// Equal to [`MessageFullName::FULL_NAME`](crate::MessageFullName::FULL_NAME) + /// for generated messages — both come from the same `proto_fqn` in + /// codegen. Hand-written impls that implement both traits must keep the + /// two strings in sync; a mismatch surfaces as a runtime panic on + /// extension access rather than at compile time. + /// + /// If you only need the message's full name (no extension access), prefer + /// [`MessageFullName::FULL_NAME`](crate::MessageFullName::FULL_NAME): it + /// is a standalone trait that doesn't require the extension machinery or + /// the `unknown_fields=true` codegen option (which adds an + /// `__buffa_unknown_fields` storage field to the generated struct). const PROTO_FQN: &'static str; /// Immutable access to the extendee's unknown-field storage. diff --git a/buffa/src/lib.rs b/buffa/src/lib.rs index 77e888e..78bb575 100644 --- a/buffa/src/lib.rs +++ b/buffa/src/lib.rs @@ -83,6 +83,7 @@ //! | Type | Purpose | //! |------|---------| //! | [`Message`] | Core trait for encode / decode / merge | +//! | [`MessageFullName`] | Compile-time `FULL_NAME` const (`"pkg.Msg"`) for generic dispatch | //! | [`DecodeOptions`] | Configurable recursion and size limits | //! | [`MessageField`](MessageField) | Optional sub-message with transparent `Deref` to default | //! | [`EnumValue`](EnumValue) | Open enum wrapper (`Known(E)` / `Unknown(i32)`) | @@ -199,7 +200,7 @@ pub mod view; pub use enumeration::{EnumValue, Enumeration}; pub use error::{DecodeError, EncodeError}; pub use extension::{Extension, ExtensionCodec, ExtensionSet}; -pub use message::{DecodeOptions, Message, RECURSION_LIMIT}; +pub use message::{DecodeOptions, Message, MessageFullName, RECURSION_LIMIT}; pub use message_field::{DefaultInstance, MessageField}; pub use oneof::Oneof; pub use size_cache::SizeCache; diff --git a/buffa/src/message.rs b/buffa/src/message.rs index 248b7fb..50c5043 100644 --- a/buffa/src/message.rs +++ b/buffa/src/message.rs @@ -417,6 +417,40 @@ pub trait Message: DefaultInstance + Clone + PartialEq + Send + Sync { fn clear(&mut self); } +/// 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 `buffa::MessageFullName` into scope to use `MyMessage::FULL_NAME`. +/// Without the trait in scope, use +/// `::FULL_NAME`. +/// +/// Codegen emits this for every generated message. Hand-written [`Message`] +/// implementations can opt in by also implementing `MessageFullName`; it is +/// a separate trait specifically so that omitting it stays non-breaking. +/// +/// For messages that also implement [`ExtensionSet`](crate::ExtensionSet), +/// this value is guaranteed equal to +/// [`ExtensionSet::PROTO_FQN`](crate::ExtensionSet::PROTO_FQN) — both come +/// from the same `proto_fqn` source in codegen. +/// +/// Because the only item is an associated `const`, this trait is **not** +/// object-safe (`dyn MessageFullName` does not compile). Use it as a +/// generic bound (`fn foo()`), not a trait object. +pub trait MessageFullName: Message { + /// The protobuf full name for the generated message type. + /// + /// Fully-qualified, with no leading dot. For a message `Foo` declared in + /// package `my.pkg` this is `"my.pkg.Foo"`. For a nested message + /// `my.pkg.Outer.Inner` it is `"my.pkg.Outer.Inner"`. For a message in + /// the unnamed root package it is the bare message name (e.g. `"Foo"`). + /// + /// Resolves at compile time — `T::FULL_NAME` is a `&'static str` literal + /// in the binary's read-only data, with no runtime cost. + const FULL_NAME: &'static str; +} + /// Options for configuring message decoding behavior. /// /// Use this to set custom recursion depth limits or maximum message sizes diff --git a/docs/guide.md b/docs/guide.md index abca78d..491fc96 100644 --- a/docs/guide.md +++ b/docs/guide.md @@ -1555,6 +1555,13 @@ Note what's *not* needed: - **`UnknownFields`** — omitted since this is a simple leaf type where round-trip preservation of unknown fields isn't important. Unknown tags are silently skipped via `skip_field`. - **Any size-caching field** — sizes live in the external `SizeCache` threaded through `compute_size` / `write_to`. A leaf type like this doesn't touch the cache; types with nested message fields reserve a slot before recursing (see the `compute_size` comment above). +- **`MessageFullName`** — opt-in. Implement it on your extern-mapped type if you have generic code that dispatches on `T::FULL_NAME` (event stores, type registries, `Any` type-URL construction); otherwise leave it off: + + ```rust,ignore + impl buffa::MessageFullName for Int64Range { + const FULL_NAME: &'static str = "my.common.Int64Range"; + } + ``` ### View types for custom implementations diff --git a/examples/logging/src/gen/buffa.examples.context.v1.mod.rs b/examples/logging/src/gen/buffa.examples.context.v1.mod.rs index 571428b..1f41483 100644 --- a/examples/logging/src/gen/buffa.examples.context.v1.mod.rs +++ b/examples/logging/src/gen/buffa.examples.context.v1.mod.rs @@ -5,6 +5,7 @@ include!("context.v1.context.rs"); non_camel_case_types, dead_code, unused_imports, + unused_qualifications, clippy::derivable_impls, clippy::match_single_binding, clippy::uninlined_format_args, diff --git a/examples/logging/src/gen/buffa.examples.log.v1.mod.rs b/examples/logging/src/gen/buffa.examples.log.v1.mod.rs index c6c5cfa..70fc688 100644 --- a/examples/logging/src/gen/buffa.examples.log.v1.mod.rs +++ b/examples/logging/src/gen/buffa.examples.log.v1.mod.rs @@ -5,6 +5,7 @@ include!("log.v1.log.rs"); non_camel_case_types, dead_code, unused_imports, + unused_qualifications, clippy::derivable_impls, clippy::match_single_binding, clippy::uninlined_format_args, diff --git a/examples/logging/src/gen/mod.rs b/examples/logging/src/gen/mod.rs index 03ada66..b64f0d8 100644 --- a/examples/logging/src/gen/mod.rs +++ b/examples/logging/src/gen/mod.rs @@ -1,25 +1,25 @@ // @generated by buffa-codegen. DO NOT EDIT. -#![allow(non_camel_case_types, dead_code, unused_imports, clippy::derivable_impls, clippy::match_single_binding, clippy::uninlined_format_args, clippy::doc_lazy_continuation, clippy::module_inception)] +#![allow(non_camel_case_types, dead_code, unused_imports, unused_qualifications, clippy::derivable_impls, clippy::match_single_binding, clippy::uninlined_format_args, clippy::doc_lazy_continuation, clippy::module_inception)] -#[allow(non_camel_case_types, dead_code, unused_imports, clippy::derivable_impls, clippy::match_single_binding, clippy::uninlined_format_args, clippy::doc_lazy_continuation, clippy::module_inception)] +#[allow(non_camel_case_types, dead_code, unused_imports, unused_qualifications, clippy::derivable_impls, clippy::match_single_binding, clippy::uninlined_format_args, clippy::doc_lazy_continuation, clippy::module_inception)] pub mod buffa { use super::*; - #[allow(non_camel_case_types, dead_code, unused_imports, clippy::derivable_impls, clippy::match_single_binding, clippy::uninlined_format_args, clippy::doc_lazy_continuation, clippy::module_inception)] + #[allow(non_camel_case_types, dead_code, unused_imports, unused_qualifications, clippy::derivable_impls, clippy::match_single_binding, clippy::uninlined_format_args, clippy::doc_lazy_continuation, clippy::module_inception)] pub mod examples { use super::*; - #[allow(non_camel_case_types, dead_code, unused_imports, clippy::derivable_impls, clippy::match_single_binding, clippy::uninlined_format_args, clippy::doc_lazy_continuation, clippy::module_inception)] + #[allow(non_camel_case_types, dead_code, unused_imports, unused_qualifications, clippy::derivable_impls, clippy::match_single_binding, clippy::uninlined_format_args, clippy::doc_lazy_continuation, clippy::module_inception)] pub mod context { use super::*; - #[allow(non_camel_case_types, dead_code, unused_imports, clippy::derivable_impls, clippy::match_single_binding, clippy::uninlined_format_args, clippy::doc_lazy_continuation, clippy::module_inception)] + #[allow(non_camel_case_types, dead_code, unused_imports, unused_qualifications, clippy::derivable_impls, clippy::match_single_binding, clippy::uninlined_format_args, clippy::doc_lazy_continuation, clippy::module_inception)] pub mod v1 { use super::*; include!("buffa.examples.context.v1.mod.rs"); } } - #[allow(non_camel_case_types, dead_code, unused_imports, clippy::derivable_impls, clippy::match_single_binding, clippy::uninlined_format_args, clippy::doc_lazy_continuation, clippy::module_inception)] + #[allow(non_camel_case_types, dead_code, unused_imports, unused_qualifications, clippy::derivable_impls, clippy::match_single_binding, clippy::uninlined_format_args, clippy::doc_lazy_continuation, clippy::module_inception)] pub mod log { use super::*; - #[allow(non_camel_case_types, dead_code, unused_imports, clippy::derivable_impls, clippy::match_single_binding, clippy::uninlined_format_args, clippy::doc_lazy_continuation, clippy::module_inception)] + #[allow(non_camel_case_types, dead_code, unused_imports, unused_qualifications, clippy::derivable_impls, clippy::match_single_binding, clippy::uninlined_format_args, clippy::doc_lazy_continuation, clippy::module_inception)] pub mod v1 { use super::*; include!("buffa.examples.log.v1.mod.rs");