Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,20 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
`buffa_build::Config::compile()` call — deconfliction cannot span separate
compilations, since each only sees its own descriptor set.

- **Per-type `extern_path` mappings were silently ignored (#111).** An
`extern_path` entry naming a single type FQN (e.g.
`.extern_path(".google.protobuf.Timestamp", "::my_types::Timestamp")`, the
prost/tonic idiom) parsed but never matched, because resolution only
considered package prefixes. Type references now resolve per-type: an exact
type-FQN entry wins over the internal `descriptor.proto` routing, which wins
over the longest matching package prefix, which wins over local generation.
Nested types inherit an enclosing message's override, resolving to the
override's parent module plus the usual `snake_case(MessageName)`
nested-types module. Note that entries which previously had no effect now
take effect: a type-FQN entry (including a typo'd one) that was a silent
no-op before will now change the generated reference, and a wrong target
surfaces as a compile error in the generated code.

## [0.6.0] - 2026-05-15

### Added
Expand Down
53 changes: 44 additions & 9 deletions buffa-build/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -451,21 +451,56 @@ impl Config {

/// Declare an external type path mapping.
///
/// Types under the given protobuf path prefix will reference the specified
/// Rust module path instead of being generated. This allows shared proto
/// packages to be compiled once in a dedicated crate and referenced from
/// others.
///
/// `proto_path` is a fully-qualified protobuf package path, e.g.,
/// `".my.common"` or `"my.common"` (the leading dot is optional and will
/// be added automatically). `rust_path` is the Rust module path where
/// those types are accessible (e.g., `"::common_protos"`).
/// The matched types reference the specified Rust path instead of being
/// generated. This allows shared proto packages to be compiled once in a
/// dedicated crate and referenced from others.
///
/// `proto_path` is a fully-qualified protobuf path — either a **package**
/// (`".my.common"`, mapping every type under it to a Rust module root) or a
/// single **type FQN** (`".google.protobuf.Timestamp"`, mapping just that
/// type, the prost/tonic idiom). The leading dot is optional and is added
/// automatically. As in prost, the most specific entry wins: an exact type
/// FQN beats a covering package prefix, which in turn beats a shorter
/// prefix.
///
/// `rust_path` is where the type(s) are accessible — a module root for a
/// package mapping (e.g. `"::common_protos"`) or a full type path for a
/// per-type mapping (e.g. `"::pbjson_types::Timestamp"`). It must be an
/// absolute path (starting with `::` or `crate::`); any other value is
/// emitted into the generated code verbatim and will fail to resolve there.
///
/// **Nested types** inherit an enclosing message's per-type override:
/// mapping `.my.pkg.Outer` to `::ext::Outer` resolves `.my.pkg.Outer.Inner`
/// to `::ext::outer::Inner` — the override's parent module plus buffa's
/// usual `snake_case(MessageName)` nested-types module (snake case of the
/// *proto* message name, regardless of the override's final segment). This
/// matches the layout of another buffa-generated crate; for a target crate
/// laid out differently, add explicit per-type entries for the nested types
/// as well.
///
/// # Limitations
///
/// An extern type that is referenced by a generated **view** must map to
/// another buffa-generated crate — the view path is composed as
/// `<rust_path_root>::__buffa::view::…`, which a non-buffa crate (e.g.
/// `pbjson_types`) does not provide. Map per-type to a buffa crate, or
/// disable views ([`generate_views(false)`](Self::generate_views)), for
/// such types.
///
/// A misconfigured mapping (a typo'd FQN target, a non-absolute
/// `rust_path`, or a view-referenced type mapped to a non-buffa crate) is
/// not diagnosed at generation time; it surfaces as an unresolved-path
/// error when the generated code is compiled.
///
/// # Example
///
/// ```rust,ignore
/// buffa_build::Config::new()
/// // Whole-package mapping.
/// .extern_path(".my.common", "::common_protos")
/// // Per-type mapping (issue #111) — overrides the package prefix for
/// // just this type.
/// .extern_path(".google.protobuf.Timestamp", "::common_protos::well_known::Timestamp")
/// .files(&["proto/my_service.proto"])
/// .includes(&["proto/"])
/// .compile()
Expand Down
Loading
Loading