Skip to content

descriptor: add linked descriptor types and dedup feature resolution#129

Merged
iainmcgin merged 3 commits into
mainfrom
reflect/02-desc
May 20, 2026
Merged

descriptor: add linked descriptor types and dedup feature resolution#129
iainmcgin merged 3 commits into
mainfrom
reflect/02-desc

Conversation

@iainmcgin
Copy link
Copy Markdown
Collaborator

What

Two changes that lay the foundation for runtime reflection (DescriptorPool + DynamicMessage, in subsequent PRs).

Linked descriptor types (buffa-descriptor/src/desc.rs)

MessageDescriptor, FieldDescriptor, FieldKind, SingularKind, OneofDescriptor, EnumDescriptor, EnumValueDescriptor, ServiceDescriptor, MethodDescriptor — the processed, feature-resolved form of the raw FileDescriptorProto tree. Where the raw protos use string type_name references and unresolved FeatureSet options, these types use pool indices (MessageIndex, EnumIndex, ServiceIndex) and pre-resolved edition features (presence, packed, delimited, enum openness).

FieldKind flattens protobuf's orthogonal type × label × map-entry axes into a single Copy discriminant that maps 1:1 to runtime representation — the same approach protobuf-es takes with its fieldKind union. This is the descriptor abstraction Buf suggested: a regular Rust struct instead of trying to work with the raw MessageDescriptor protos.

Fields are private with #[inline] accessor methods, matching the buffa convention for hand-written API types (SizeCache, UnknownFields, Tag). Construction is gated to DescriptorPool (next PR) — downstream test fixtures go through DescriptorPool::decode from FDS bytes, so they don't skip the feature-resolution and validation passes.

Feature resolution dedup

The shared core (file/message/enum/oneof feature resolution, edition defaults, FeatureSet merge) moves from buffa-codegen/src/features.rs to buffa-descriptor/src/features.rs and is re-exported from buffa-codegen. buffa-codegen retains the codegen-only resolve_field, which overlays the referenced enum's enum_type from CodeGenContext::is_enum_closed — a lookup built during codegen and not available to a runtime pool.

A divergence between codegen and the runtime pool would mean generated code and reflective code disagree on packed encoding, presence, or enum openness — sharing the implementation makes that impossible.

Verification

  • task gen-wkt-types produces no diff (the features.rs move is byte-identical to consumers).
  • All 4 conformance modes pass.
  • no_std build of buffa-descriptor clean.

Net change

+737/-534. The buffa-codegen/src/features.rs deletions are the moved implementation; the additions are desc.rs (~700 lines including ~150 of unit tests) and the relocated features.rs.

iainmcgin added 2 commits May 20, 2026 04:15
Adds a #[doc(hidden)] buffa::json_helpers::wkt module with the shared
formatting and parsing primitives for the well-known types' JSON forms:
Timestamp RFC 3339 (fmt_timestamp, parse_timestamp), Duration decimal
seconds (fmt_duration, parse_duration, validate_duration), FieldMask
camelCase (snake_to_camel, camel_to_snake, field_mask_path_round_trips),
and the Howard Hinnant civil-calendar helpers (days_to_date,
date_to_days).

buffa-types' typed serde impls (Timestamp, Duration, FieldMask) now call
into this module rather than carrying their own implementations. The
adapters preserve the Option-returning private API the existing test
suite (~50 call sites) targets, so no test churn.

Sharing the implementation is load-bearing: the conformance suite
exercises the typed JSON path today, and a forthcoming reflective JSON
codec on DynamicMessage will exercise the same forms. A divergence
between the two (one accepting a fractional-second precision the other
rejects, or two civil-calendar implementations disagreeing on a
leap-year edge) would be a user-visible inconsistency. With the
implementation shared, drift is impossible.

The module is #[doc(hidden)] because the supported entry points are the
typed serde impls and (forthcoming) DynamicMessage's JSON codec — these
helpers operate on raw scalars and have no semver contract.
Two changes that lay the foundation for runtime reflection.

Linked descriptor types (buffa-descriptor/src/desc.rs):

MessageDescriptor, FieldDescriptor, FieldKind, SingularKind,
OneofDescriptor, EnumDescriptor, EnumValueDescriptor, ServiceDescriptor,
MethodDescriptor — the processed, feature-resolved form of the raw
FileDescriptorProto tree. Where the raw protos use string type_name
references and unresolved FeatureSet options, these types use pool
indices (MessageIndex, EnumIndex, ServiceIndex) and pre-resolved
edition features (presence, packed, delimited, enum openness).

FieldKind flattens protobuf's orthogonal type x label x map-entry axes
into a single Copy discriminant that maps 1:1 to runtime
representation, the same approach protobuf-es takes with its fieldKind
union.

Fields are private with #[inline] accessor methods, matching the buffa
convention for hand-written API types (SizeCache, UnknownFields, Tag).
Construction is gated to DescriptorPool (forthcoming) — downstream
test fixtures go through DescriptorPool::decode from FDS bytes, so
they don't skip the feature-resolution and validation passes.

Field indices within a message are u16, capping fields-per-message at
65,535. Field numbers stay u32 per the protobuf spec.

Feature resolution dedup:

The shared core (file/message/enum/oneof feature resolution, edition
defaults, FeatureSet merge) moves from buffa-codegen/src/features.rs to
buffa-descriptor/src/features.rs and is re-exported from buffa-codegen.
buffa-codegen retains the codegen-only resolve_field, which overlays
the referenced enum's enum_type from CodeGenContext::is_enum_closed —
a lookup built during codegen and not available to a runtime pool.

A divergence between codegen and the runtime pool would mean generated
code and reflective code disagree on packed encoding, presence, or
enum openness — sharing the implementation makes that impossible.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 20, 2026

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

Base automatically changed from reflect/01-wkt-helpers to main May 20, 2026 19:58
@iainmcgin iainmcgin marked this pull request as ready for review May 20, 2026 20:11
@iainmcgin iainmcgin requested a review from azdagron May 20, 2026 20:11
@iainmcgin iainmcgin enabled auto-merge (squash) May 20, 2026 20:23
@iainmcgin iainmcgin merged commit 336b200 into main May 20, 2026
7 checks passed
@iainmcgin iainmcgin deleted the reflect/02-desc branch May 20, 2026 20:24
@github-actions github-actions Bot locked and limited conversation to collaborators May 20, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants