Problem
extern_path only matches at the proto package prefix level — a mapping for a specific type FQN is silently ignored. This is the same gap as anthropics/buffa#111, surfacing through connectrpc-codegen because it reuses buffa-codegen's type resolution.
This came up while working with Buf on the BSR Cargo SDK plugins. BSR's SDK assembly injects extern_path mappings to point a downstream plugin's generated code at the upstream module's SDK crate. Module-level (package-prefix) injection works today; per-type injection does not, which prost/tonic support and which BSR could rely on if a single proto package's files were ever split across more than one BSR module.
Where the limitation lives
TypeResolver (connectrpc-codegen/src/codegen.rs) wraps buffa_codegen::context::CodeGenContext::for_generate() so that service-stub input/output paths are byte-identical to what buffa emits for message fields:
struct TypeResolver<'a> {
ctx: buffa_codegen::context::CodeGenContext<'a>,
require_extern: bool,
}
impl<'a> TypeResolver<'a> {
fn new(...) -> Self {
Self {
ctx: buffa_codegen::context::CodeGenContext::for_generate(proto_file, file_to_generate, config),
require_extern,
}
}
}
CodeGenContext matches extern_paths against each file's package, never against a type FQN, so a per-type mapping like extern_path=.google.protobuf.Timestamp=::other_crate::Ts is dead — see anthropics/buffa#111 for the details and the prost comparison.
What this needs in connect-rust
The fix lands in buffa-codegen and connectrpc-codegen should inherit it for free via for_generate() — that's the whole point of the TypeResolver design. But there is connect-rust-specific surface to verify once buffa supports per-type matching:
TypeResolver::resolve_path / rust_type — service method input/output owned types under a per-type override.
TypeResolver::rust_view_type — view paths use CodeGenContext::rust_type_relative_split to find the package boundary and insert the __buffa::view:: sentinel; a per-type override changes where that boundary falls.
- The
require_extern mode used by generate_services — it currently rejects any path that isn't ::/crate::-rooted with an error message that suggests extern_path=.=<…>. The error text and the checks should still hold up when a per-type override is present.
- Tests: extend
wkt_types_use_buffa_types_extern_path and extern_catchall_with_wkt_longest_wins with a per-type override case; add a nested-type case.
Status
Tracking only. Blocked on anthropics/buffa#111. No implementation planned until Buf confirms the per-type case is something BSR's SDK assembly actually emits — module-level extern_path injection has been sufficient for the BSR plugin work so far.
Problem
extern_pathonly matches at the proto package prefix level — a mapping for a specific type FQN is silently ignored. This is the same gap as anthropics/buffa#111, surfacing throughconnectrpc-codegenbecause it reuses buffa-codegen's type resolution.This came up while working with Buf on the BSR Cargo SDK plugins. BSR's SDK assembly injects
extern_pathmappings to point a downstream plugin's generated code at the upstream module's SDK crate. Module-level (package-prefix) injection works today; per-type injection does not, which prost/tonic support and which BSR could rely on if a single proto package's files were ever split across more than one BSR module.Where the limitation lives
TypeResolver(connectrpc-codegen/src/codegen.rs) wrapsbuffa_codegen::context::CodeGenContext::for_generate()so that service-stub input/output paths are byte-identical to what buffa emits for message fields:CodeGenContextmatchesextern_pathsagainst each file's package, never against a type FQN, so a per-type mapping likeextern_path=.google.protobuf.Timestamp=::other_crate::Tsis dead — see anthropics/buffa#111 for the details and the prost comparison.What this needs in connect-rust
The fix lands in
buffa-codegenandconnectrpc-codegenshould inherit it for free viafor_generate()— that's the whole point of theTypeResolverdesign. But there is connect-rust-specific surface to verify once buffa supports per-type matching:TypeResolver::resolve_path/rust_type— service method input/output owned types under a per-type override.TypeResolver::rust_view_type— view paths useCodeGenContext::rust_type_relative_splitto find the package boundary and insert the__buffa::view::sentinel; a per-type override changes where that boundary falls.require_externmode used bygenerate_services— it currently rejects any path that isn't::/crate::-rooted with an error message that suggestsextern_path=.=<…>. The error text and the checks should still hold up when a per-type override is present.wkt_types_use_buffa_types_extern_pathandextern_catchall_with_wkt_longest_winswith a per-type override case; add a nested-type case.Status
Tracking only. Blocked on anthropics/buffa#111. No implementation planned until Buf confirms the per-type case is something BSR's SDK assembly actually emits — module-level extern_path injection has been sufficient for the BSR plugin work so far.