descriptor: custom options, Any pack/unpack, and symbol-to-file lookup#140
Merged
Conversation
|
All contributors have signed the CLA ✍️ ✅ |
fbe41fe to
2d0c3a6
Compare
3f5d534 to
ba7f3f1
Compare
The final reflection-parity items against protobuf-go and protobuf-es. Custom options: each linked descriptor (Field/Message/Enum/EnumValue/ Service/Method/Oneof) exposes options() -> Option<&XxxOptions>, the raw generated options message cloned (boxed) at link time — None for the common no-options case. Matches Go's desc.Options(). Custom options are extensions of the *Options message and survive on its unknown fields; DynamicMessage::from_options(pool, field.options()?) re-decodes the options as a DynamicMessage of the corresponding google.protobuf.*Options type (resolved by MessageName::FULL_NAME), so dyn_opts.get(ext.field()) reads a custom option by name with no hardcoded type strings. Requires descriptor.proto plus the option-defining proto in the pool. Any pack/unpack: DynamicMessage::pack_any / unpack_any, the binary counterpart to the Any JSON @type expansion (CEL dyn evaluation). unpack_any resolves the type_url's last segment against the pool and decodes the value bytes; pack_any wraps with the type.googleapis.com/ prefix. AnyError (#[non_exhaustive], Clone) distinguishes NotAny / AnyNotRegistered / MissingTypeUrl / UnknownType / Decode { type_url, source }. Symbol → file: DescriptorPool::file_containing_symbol is now backed by a precomputed symbol_file index (O(log n), replacing an O(symbols × files) parent-walk scan that only covered messages and enums). Indexes the full set gRPC server reflection's FindFileContainingSymbol accepts: messages, fields, oneofs, enums, enum values (parent scope per protobuf naming), services, methods, and extensions. Unblocks a ServerReflection adapter in connect-rust. Conformance: reflect suite holds at 2783 successes, 0 expected failures, 0 unexpected. 22 new e2e tests across options/Any/symbol.
ba7f3f1 to
ca8f3e5
Compare
azdagron
approved these changes
May 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The final three reflection-parity items against protobuf-go / protobuf-es, so the answer to "what does Go's reflection do that buffa's doesn't" is only ever the documented deferrals (proto2 declared defaults, text format on dynamic messages, descriptor-tree navigation).
1. Custom options
Each linked descriptor (
Field/Message/Enum/EnumValue/Service/Method/Oneof) exposesoptions() -> Option<&XxxOptions>— the raw generated options message, boxed and cloned at link time (Nonefor the common no-options case). Matches Go'sdesc.Options().Custom options are extensions of the
*Optionsmessage and survive on its unknown fields.DynamicMessage::from_options(pool, field.options()?)re-decodes the options as aDynamicMessageof the correspondinggoogle.protobuf.*Optionstype (resolved byMessageName::FULL_NAME), sodyn_opts.get(ext.field())reads a custom option by name — no hardcoded type strings. Requiresdescriptor.protoplus the option-defining proto in the pool. This is the headline use case (protovalidate,(google.api.http)transcoding, PII annotations) and leans entirely on the extension machinery from #139.2. Any pack/unpack
DynamicMessage::pack_any/unpack_any— the binary counterpart to theAnyJSON@typeexpansion, for CELdynevaluation.unpack_anyresolves thetype_url's last path segment against the pool and decodes thevaluebytes;pack_anywraps a message with thetype.googleapis.com/prefix.AnyError(#[non_exhaustive],Clone) distinguishesNotAny/AnyNotRegistered/MissingTypeUrl/UnknownType/Decode { type_url, source }.3. Symbol → file
DescriptorPool::file_containing_symbolis now backed by a precomputedsymbol_fileindex — O(log n), replacing an O(symbols × files) parent-walk scan that only covered messages and enums. It indexes the full set gRPC server reflection'sFindFileContainingSymbolaccepts: messages, fields, oneofs, enums, enum values (in their parent scope per protobuf naming), services, methods, and extensions. Unblocks aServerReflectionadapter in connect-rust.Verification
reflect-only no_std clean, fmt clean, doctests pass.Net change
+~560/-36. Stacked on #139 (extension reflection) — the custom-option read path depends on it.