Conversation
…age .mod.rs stitcher
Views, oneof enums, file-level extensions, and register_types() now live
under a single reserved `buffa_::` module per package instead of being
interleaved with owned types. Oneof enums drop the Oneof/View suffix
(tree position disambiguates); ViewNameConflict and OneofNameConflict
are gone — collisions with proto names are structurally impossible. The
sentinel is the only name reserved in user namespace and is checked.
Each .proto emits five content files; each package emits one
`<pkg>.mod.rs` stitcher that authors `pub mod buffa_ { ... }` and
include!s the content files. Consumers wire up only the stitcher via
`buffa::include_proto!("pkg")`. register_types is now naturally one fn
per package, fixing the multi-file-same-package collision (e.g. seven
WKT files in google.protobuf).
Regenerated WKT/bootstrap/logging-example output; migrated all
in-repo consumers (buffa-test, conformance, examples, benchmarks,
packaging plugin, googleapis stress).
- generate_package_mod: rebuild via quote! + prettyplease (was string concat); reuse format_tokens - @generated header: protoc-gen-buffa → buffa-codegen (runs under buffa-build too) - ALLOW_LINTS: single pub const at crate root; consumed by generate_module_tree, the .mod.rs stitcher, and buffa-build - generate_module_tree: take IncludeMode {Relative, OutDir}; buffa-build's generate_include_file now delegates to it instead of duplicating tree-building - validate_file: merge the three reserved-name/module-conflict checks into one walk - view.rs owned-path lookup: ? with CodeGenError::Other instead of a silent fallback - rust_type_relative_split extern branch: debug_assert on the segment-count invariant + two unit tests - ancillary_prefix: build super:: chain via quote! loop, not syn::parse_str - empty-package stitcher filename: use the reserved sentinel (buffa_.mod.rs) so it provably can't collide with a real package Regenerated WKT/bootstrap/logging .mod.rs files (prettyplease formatting change only).
The buffa_:: canonical path is intentionally the only path. Convenience re-exports would make short paths work in the common case but silently change meaning when a user proto shadows them — predictability over brevity.
- generate_module_tree: drop .unwrap() on infallible String fmt::Write; accept &[(impl AsRef<str>, impl AsRef<str>)] so callers pass owned vecs directly (drops the re-borrow shim in buffa-build and packaging) - GeneratedFile / GeneratedFileKind / include_proto! docs: clarify that consumers wire up only PackageMod entries and that $pkg is the dotted proto package literal (empty package → "buffa_") - emit_register_fn doc: reflect per-package stitcher emission (the old multi-file collision rationale is obsolete) - ancillary_prefix: debug_assert dotless-FQN convention + doc note - RegistryPaths::is_empty: one-line doc
validate_file checked package segments and message names against the
reserved sentinel but not file-level enum names. Enum type names are
emitted verbatim at package root, so `enum buffa_ { ... }` would land
beside `pub mod buffa_` (E0428). Nested enums live inside their owner's
module and cannot collide, so only file-level is checked. Adds two
naming.rs tests (rejected file-level / allowed nested).
Also: protoc-gen-buffa-packaging module doc still described per-file
output and referenced the removed proto_path_to_rust_module; rewrite
for the per-package .mod.rs stitcher model.
|
[claude code] Renamed the sentinel from Also added two regression tests for the collision cases this PR fixes: I'll close #54 once this lands. |
Aligns the sentinel with the existing reserved field-name prefix
(__buffa_cached_size, __buffa_unknown_fields) so the rule is uniformly
"anything starting __buffa is reserved by codegen". The trailing-
underscore form was a distinct second pattern.
Mechanical rename via SENTINEL_MOD constant; consumer paths and doc
strings updated. Validator inputs adjusted (`__Buffa` snake_cases to
`__buffa`; `Buffa_` no longer does). Adds two regression tests:
- generation.rs: `package foo` + `package foo.view` produces two
stitchers with the kind tree under __buffa, not as a top-level
`pub mod view` sibling.
- prelude_shadow.proto: `message View { Inner }` compiles end-to-end.
Regenerated WKT, bootstrap descriptor, and logging-example checked-in
code.
893f7bd to
4bd7a6a
Compare
iainmcgin
left a comment
There was a problem hiding this comment.
[claude code] Reviewed: sentinel rename clean (single SENTINEL_MOD constant), validator covers package segments / messages / file-level enums with the right snake-case vs verbatim split, regression tests cover both collision repros end-to-end. Per-package .mod.rs stitcher + include_proto! macro + canonical ALLOW_LINTS eliminates the wrapper duplication. CI green at 4bd7a6a.
Summary
Ancillary types emitted by
buffa-codegen— view structs, oneof enums, oneof view enums, extension consts, andregister_types— currently share the package-level Rust namespace with user proto types. Suffix-based disambiguation (FooView,KindOneof) handles most cases but still rejects pathological inputs (message FooViewnext tomessage Foo; nested type literally named{X}Oneofnext tooneof {x}) with a codegen error.This PR moves all ancillary kinds under a single reserved sentinel module
__buffa::per package, making collisions structurally impossible without name mangling, and has codegen emit a per-package<pkg>.mod.rsstitcher so consumers integrate via onebuffa::include_proto!("pkg")call instead of hand-authoring the module wrapper.Supersedes #54 and #37. Closes #32.
Layout
pkg::Foopkg::Foo(unchanged)pkg::FooViewpkg::__buffa::view::FooViewpkg::foo::BarViewpkg::__buffa::view::foo::BarViewpkg::foo::KindOneofpkg::__buffa::oneof::foo::Kindpkg::foo::KindOneofViewpkg::__buffa::view::oneof::foo::Kindpkg::FOOpkg::__buffa::ext::FOOpkg::register_types(per file)pkg::__buffa::register_types(per package)The sentinel
__buffais the only name codegen reserves in user namespace. It aligns with the existing__buffa_reserved-field-name prefix (__buffa_cached_size,__buffa_unknown_fields), so the rule is uniformly "anything starting__buffais reserved."validate_fileerrors withCodeGenError::ReservedModuleNameif any proto package segment, message, or file-level enum would produce a__buffa-named module/type. Adding future ancillary kinds nests them under the existing sentinel; no new reserved words.No convenience re-exports. Short-path aliases (
pub use __buffa::view::*) are intentionally not emitted — they would work in the common case but silently change meaning when a user proto shadows them. The canonical__buffa::path is unconditional.Per-package
.mod.rsstitcherCodegen now emits, in addition to the five per-proto content files (
<stem>.rs,<stem>.__view.rs,<stem>.__oneof.rs,<stem>.__view_oneof.rs,<stem>.__ext.rs), one<dotted.pkg>.mod.rsper package containing thepub mod __buffa { … }wrapper and a single mergedregister_typesbody. Consumers reference only the stitcher:Per-proto content files remain 1:1 with input protos (split-invocation safe for build systems that compile package subsets independently). The stitcher is the only per-package artifact and has the same caveat as any generated include file.
This collapses the wrapper-authoring previously duplicated across
buffa-build,buffa-types,buffa-test,conformance, examples, benchmarks,gen_lib_rs.py, andprotoc-gen-buffa-packagingto one place in codegen (the canonicalALLOW_LINTSlist lives there too).buffa-build::generate_include_filenow delegates tobuffa_codegen::generate_module_tree.register_typesper-packageBecause the stitcher is per-package,
register_typesis naturally one fn per package. Theemit_register_fn=falseworkaround ingen_wkt_types.rs(seven WKT files × one fn each colliding ingoogle.protobuf) is no longer needed.Deleted
CodeGenError::OneofNameConflictand::ViewNameConflict— collisions are structurally impossible.proto_path_to_rust_moduleis#[deprecated](consumers should use the stitcher path); follow-up will remove it.Test plan
task lint— fmt + clippy clean (verified against rustc 1.95)task lint-md— markdownlint cleantask test— 1462 tests passtask verify— 6×CONFORMANCE SUITE PASSED, baseline numbers exacttask stress-googleapis— 47 protos compile cleanmessage View { message Inner {} }(codegen unit +prelude_shadow.protoend-to-end) andpackage foo.view;package foo.__buffa;,message __Buffa {}, andenum __buffa {}Breaking changes
API-breaking for downstream consumers of generated code (import paths for views, oneofs, extensions,
register_types). Migration is mechanical per the layout table above. Pre-1.0.