Background
connectrpc_codegen::codegen::Options currently mirrors a hand-picked subset of buffa_codegen::CodeGenConfig fields one-by-one (strict_utf8_mapping, generate_json, extern_paths, and as of #TBD, emit_register_fn). Options::to_buffa_config() then copies each into a fresh CodeGenConfig.
This was fine while only two or three knobs mattered, but every new buffa option (generate_text, generate_arbitrary, bytes_fields, preserve_unknown_fields, allow_message_set, …) requires:
- a new field on
Options,
- a new line in
to_buffa_config(),
- a new builder method on
connectrpc_build::Config,
- a new plugin parameter token in
generate(),
- a CHANGELOG entry, and a release.
A recent example: a downstream user needed emit_register_fn=false to suppress the per-file register_types collision when include!ing multiple generated files into the same module — a one-line buffa change that required a five-touchpoint plumbing PR and a patch release.
Proposal
For v0.4.0, replace per-field forwarding with direct passthrough of buffa's CodeGenConfig. Several shapes are viable:
A. Embed the buffa config in Options:
pub struct Options {
pub buffa: buffa_codegen::CodeGenConfig,
pub extern_paths: Vec<(String, String)>, // still set by plugin parser
// ...connectrpc-specific fields
}
connectrpc_build::Config gains fn buffa_config(self, cfg: CodeGenConfig) -> Self. Existing convenience methods (generate_json, strict_utf8_mapping) stay as thin shims that mutate self.options.buffa.<field> for the common cases.
B. Escape-hatch closure:
pub fn with_buffa_config(self, f: impl FnOnce(&mut CodeGenConfig)) -> Self
Users mutate the underlying buffa config in place. Less intrusive but discoverability is worse.
Constraints to preserve in either shape:
- connectrpc forces
generate_views = true (the service stubs require view types). Whatever shape we pick must apply this override after the user's config, not before.
extern_paths is also populated by the protoc plugin's parameter parser (buffa_module=, extern_path=), so it can't simply be "whatever the user passed in".
Options is #[non_exhaustive], so the migration is non-breaking on the additive side; we'd be free to remove the per-field shims in the same release if we want to keep the surface small.
Migration
Re-export buffa_codegen::CodeGenConfig from connectrpc_codegen so downstream users don't need a direct buffa-codegen dependency.
Mark this for v0.4.0 — the per-field shims can either be removed (breaking, sweep through all of them at once) or kept as deprecated wrappers.
Background
connectrpc_codegen::codegen::Optionscurrently mirrors a hand-picked subset ofbuffa_codegen::CodeGenConfigfields one-by-one (strict_utf8_mapping,generate_json,extern_paths, and as of #TBD,emit_register_fn).Options::to_buffa_config()then copies each into a freshCodeGenConfig.This was fine while only two or three knobs mattered, but every new buffa option (
generate_text,generate_arbitrary,bytes_fields,preserve_unknown_fields,allow_message_set, …) requires:Options,to_buffa_config(),connectrpc_build::Config,generate(),A recent example: a downstream user needed
emit_register_fn=falseto suppress the per-fileregister_typescollision wheninclude!ing multiple generated files into the same module — a one-line buffa change that required a five-touchpoint plumbing PR and a patch release.Proposal
For v0.4.0, replace per-field forwarding with direct passthrough of buffa's
CodeGenConfig. Several shapes are viable:A. Embed the buffa config in
Options:connectrpc_build::Configgainsfn buffa_config(self, cfg: CodeGenConfig) -> Self. Existing convenience methods (generate_json,strict_utf8_mapping) stay as thin shims that mutateself.options.buffa.<field>for the common cases.B. Escape-hatch closure:
Users mutate the underlying buffa config in place. Less intrusive but discoverability is worse.
Constraints to preserve in either shape:
generate_views = true(the service stubs require view types). Whatever shape we pick must apply this override after the user's config, not before.extern_pathsis also populated by the protoc plugin's parameter parser (buffa_module=,extern_path=), so it can't simply be "whatever the user passed in".Optionsis#[non_exhaustive], so the migration is non-breaking on the additive side; we'd be free to remove the per-field shims in the same release if we want to keep the surface small.Migration
Re-export
buffa_codegen::CodeGenConfigfromconnectrpc_codegenso downstream users don't need a directbuffa-codegendependency.Mark this for v0.4.0 — the per-field shims can either be removed (breaking, sweep through all of them at once) or kept as deprecated wrappers.