Skip to content

Namespace ancillary codegen output into view:: and ext:: sub-modules#54

Draft
iainmcgin wants to merge 1 commit intomainfrom
prototype/codegen-namespacing
Draft

Namespace ancillary codegen output into view:: and ext:: sub-modules#54
iainmcgin wants to merge 1 commit intomainfrom
prototype/codegen-namespacing

Conversation

@iainmcgin
Copy link
Copy Markdown
Collaborator

@iainmcgin iainmcgin commented Apr 17, 2026

Summary

Ancillary types emitted by buffa-codegen — borrowed views, top-level extension consts, and the per-file registration fn — previously lived in the same Rust namespace as user-declared proto types and could collide with them. For example, a message FooView in a .proto would clash with the generated view type of sibling message Foo, and a message RegisterTypes would clash with the per-file register_types fn.

This PR moves those ancillary types into dedicated sub-modules (view::, ext::) to structurally deconflict them, and drops the Oneof suffix from oneof enums since the owner message's sub-module already disambiguates them.

Layout changes

Views move into a parallel view:: tree

Before After
pkg::FooView pkg::view::FooView
pkg::foo::BarView pkg::view::foo::BarView
pkg::foo::KindView (oneof view) pkg::view::foo::KindView

The View suffix is retained so that owned and view types can be imported into the same scope without aliasing:

use pkg::{Foo, view::FooView};
fn process(m: &Foo, v: FooView<'_>) { /* ... */ }

Extensions and package housekeeping move into ext::

Before After
pkg::FOO (extension const) pkg::ext::FOO
pkg::register_types pkg::ext::register_types

Oneof enums drop the Oneof suffix

Before After
foo::KindOneof foo::Kind

Oneof enums stay in the owner message's sub-module — they don't move to a parallel tree. The suffix was introduced to avoid name conflicts (see #33 / #34); those conflicts are now prevented by the view:: move for view-side names, and by the existing reserved-name machinery for owned-side names. The suffix is therefore redundant and was removed for readability.

Codegen internals

  • GeneratedFile gains kind: GeneratedFileKind and package: String fields. Each proto produces three sibling output files (.rs, .__view.rs, .__ext.rs).
  • generate_module_tree accepts &[ModuleTreeEntry<'_>] (was &[(&str, &str)]) and coalesces per-package streams across multiple .proto files so packages that span files (e.g. google.rpc) merge their view:: / ext:: blocks into single wrappers.
  • Checked-in generated code (WKTs + bootstrap descriptor types) was regenerated. Two bootstrap sibling stubs are empty because the bootstrap descriptor types are generated with generate_views=false.

Deconfliction wins

Two proto patterns that were previously rejected are now legal and covered by regression tests in buffa-codegen/src/tests/naming.rs:

  • oneof my_field alongside nested message MyFieldView
  • Sibling oneofs my_field and my_field_view

Migration for downstream consumers

// Before
use pkg::{Foo, FooView, FOO, register_types};
use pkg::foo::KindOneof;

// After
use pkg::Foo;
use pkg::view::FooView;
use pkg::ext::{FOO, register_types};
use pkg::foo::Kind;

Test plan

  • cargo check --workspace clean
  • task test — 1442 tests pass (18 suites), 0 failures
  • task test-codegen — 38 snapshot tests pass
  • task lintcargo fmt --check and cargo clippy --workspace --all-targets -- -D warnings both clean
  • task conformance — 6 CONFORMANCE SUITE PASSED lines (std binary+JSON + no_std binary+JSON + via-view binary + std/no_std text + via-view text), baseline numbers unchanged
  • task stress-googleapis — 141 files across ~7 packages compile cleanly (including multi-file google.rpc)
  • Two new regression tests in tests/naming.rs confirm the deconfliction wins

Diff breakdown by category

Total: 65 files, +4273 / -3326.

Category Files +lines -lines Notes
Core codegen source (buffa-codegen/src/*.rs, non-test) 5 627 251 The actual logic change — new parallel-tree path resolution (view.rs::rewrite_to_view_path), module-tree coalescing in lib.rs, GeneratedFileKind threading, oneof-suffix removal. Net +376.
Codegen tests (snapshot + integration) 7 369 217 Snapshot updates for new paths + two new regression tests for the deconfliction wins. Net +152.
buffa-build API adaptation 1 93 36 Plumbing the new ModuleTreeEntry / GeneratedFileKind through the build-script path. Net +57.
buffa-test consumer test suite 17 259 175 Mechanical import-path migration across the full end-to-end test suite. Net +84.
buffa-types hand-maintained (lib.rs, value_ext.rs, timestamp_ext.rs, roundtrip test) 4 60 31 lib.rs hand-stitches pub mod view / pub mod ext; other files adjust paths. Net +29.
Conformance runner 2 144 14 Rewrites of path references + Cargo.lock. Net +130.
protoc-gen-buffa-packaging 1 43 2 Path reference updates. Net +41.
Googleapis stress tooling (gen_lib_rs.py) 1 48 23 Updated to emit the 3-kind (owned/__view/__ext) module tree for the stress test. Net +25.
Subtotal — human-authored 38 1643 749 Net +894
Regenerated WKT types (buffa-types/src/generated/) 21 2616 2575 7 .rs files shrink as inlined view code moves to 14 new .__view.rs / .__ext.rs sibling files. Net +41.
Regenerated bootstrap descriptor types (buffa-descriptor/src/generated/) 6 14 2 Header-line updates + 4 empty sibling stubs (bootstrap uses generate_views=false). Net +12.
Subtotal — regenerated 27 2630 2577 Net +53

So of the 4273 / 3326 total, roughly 1643 / 749 (38% / 23%) is human-authored change, and 2630 / 2577 (62% / 77%) is codegen-regenerated output — where the large insertion and deletion counts largely cancel out because content moved between files rather than being added or removed on net.

The "real" change — logic-plus-tests-plus-consumers — is on the order of ~900 net lines, distributed fairly evenly across codegen internals, consumer adaptation, and test updates.

Breaking changes

API-breaking for downstream consumers of generated code. Pre-1.0 this is expected; the migration is mechanical (adjust import paths).

Moves generated types that previously collided with user-declared proto
names into dedicated sub-modules, structurally deconflicting the codegen
output from proto-derived names.

Layout changes:

- Views: `pkg::FooView` → `pkg::view::FooView` (top-level), and
  `pkg::foo::BarView` → `pkg::view::foo::BarView` (nested). Oneof view
  enums follow the same tree: `pkg::view::foo::KindView`. The `View`
  suffix is retained so that owned and view types can be imported into
  the same scope without renaming.

- Extensions: `pkg::FOO` → `pkg::ext::FOO`, and per-file
  `pkg::register_types` → `pkg::ext::register_types`.

- Oneof enums: `foo::KindOneof` → `foo::Kind`. The suffix is no longer
  needed for disambiguation; the owner message's sub-module provides the
  namespace.

Oneof enums themselves remain in the owner message's sub-module
(`pkg::foo::Kind`). The view-tree move makes several previously-illegal
proto patterns legal — two regression tests in `naming.rs` capture this.

Codegen internals:

- `GeneratedFile` gains `kind: GeneratedFileKind` + `package: String`
  fields. Each generated proto now produces three sibling output files
  (`.rs`, `.__view.rs`, `.__ext.rs`); `generate_module_tree` coalesces
  them per package so multiple `.proto` files that share a Rust module
  (e.g. `google.rpc`) merge their `view::` / `ext::` blocks cleanly.

- `generate_module_tree` signature widens from `&[(&str, &str)]` to
  `&[ModuleTreeEntry<'_>]` to carry the kind.

- `MessageScope::deeper()` is used for depth-aware path resolution in the
  parallel view tree.

Checked-in generated code regenerated (WKTs + bootstrap descriptor types).
Conformance suite, googleapis stress test, and the existing test suite
(1442 tests) all pass.
@iainmcgin iainmcgin force-pushed the prototype/codegen-namespacing branch from ae6dd3b to e46e6b9 Compare April 18, 2026 00:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant