Skip to main content

generate

Function generate 

Source
pub fn generate(request: &CodeGeneratorRequest) -> Result<CodeGeneratorResponse>
Expand description

Generate a CodeGeneratorResponse from a protoc CodeGeneratorRequest.

This is the entry point for the protoc plugin (protoc-gen-connect-rust). It parses the comma-separated request.parameter into Options and delegates to generate_services — service stubs only. Callers must run protoc-gen-buffa (or equivalent) separately for message types.

§Output

Per proto with at least one service: a <stem>.__connect.rs content file with the service stubs. Per package with at least one such proto: a <pkg>.mod.rs stitcher that include!s the content files. The stitcher filename intentionally matches protoc-gen-buffa’s, so run this plugin into a separate output directory and use protoc-gen-buffa-packaging to wire both trees, as shown in this repo’s buf.gen.yaml examples.

Under file_per_package the per-proto split is collapsed: one <dotted.pkg>.rs per package with all service stubs inlined, no per-proto content files, and no stitcher. Drop the protoc-gen-buffa-packaging invocations from your buf.gen.yaml under this layout — there are no per-file content files or stitchers for it to wire, and leaving it in produces dead mod.rs output without an error. Either let your downstream build tool synthesise the module tree from <dotted.package>.rs filenames (BSR cargo SDKs do this automatically) or hand-write the mod.rs. See generate_services.

A worked file_per_package buf.gen.yaml:

version: v2
plugins:
  - local: protoc-gen-buffa
    out: src/gen/buffa
    opt: [file_per_package]
  - local: protoc-gen-connect-rust
    out: src/gen/connect
    opt: [file_per_package, buffa_module=crate::gen::buffa]

You then mount each tree with a hand-written mod.rs (or let BSR’s cargo SDK pipeline do it):

pub mod buffa { /* one `pub mod <pkg> { include!("<pkg>.rs"); }` per package */ }
pub mod connect { /* same, pointing at src/gen/connect */ }

§Recognized options

  • buffa_module=<rust_path> — where you mounted the buffa-generated module tree (e.g. buffa_module=crate::proto). Shorthand for extern_path=.=<rust_path>. This is the option most local users want.
  • extern_path=<proto>=<rust> — map a specific proto package prefix to a Rust module path. Repeatable; longest-prefix-match wins. extern_path=.=<path> is the catch-all (equivalent to buffa_module). At least one catch-all mapping is required so every type resolves. Every mapped path must point at buffa-generated code from buffa ≥ 0.7.0 with views enabled — the stubs use the buffa::HasMessageView impls and owned-view wrappers generated with each message (buffa-types 0.7+ qualifies for the well-known types).
  • file_per_package — emit one <dotted.pkg>.rs per proto package instead of the per-proto split + stitcher. Set protoc-gen-buffa’s own file_per_package option to the same value — the BSR/tonic lib.rs synthesis assumes both plugins use the same filename convention; mismatched settings produce a valid but asymmetric layout you would have to wire by hand. Keep using a dedicated output directory (the documented split-path setup already does this) — the filename matches protoc-gen-buffa’s and would silently overwrite in a shared one. See CodeGenConfig::file_per_package for the strategy: directory constraint.
  • strict_utf8_mapping — see CodeGenConfig::strict_utf8_mapping.
  • no_json — disable serde derives on generated message types. Ignored in this plugin (no message types emitted); accepted for compatibility with the unified path.
  • no_register_fn — suppress the per-file register_types(&mut TypeRegistry) aggregator. See CodeGenConfig::emit_register_fn. Ignored in this plugin (no message types emitted); accepted for compatibility with the unified path.
  • gate_client_feature — prefix every emitted FooClient<T> struct and its impl block with #[cfg(feature = "client")].

§Client-side cfg gate

When gate_client_feature is set, the consumer crate must declare a Cargo feature literally named client. Without it, the generated FooClient items will be absent from the crate namespace.

Two consumer patterns:

  1. Dep-forwarding (client = ["connectrpc/client"], with connectrpc = { ..., features = ["server"] } and no "client" in that dep’s feature list): turns the gate into a real server-only escape hatch. Disabling the feature drops connectrpc/client (and its transport stack) from the dependency graph entirely. This is the intended use; see connectrpc-health for the minimal example.

  2. Marker (client = [], no forwarding): satisfies the gate without slimming the dependency graph. Use only when you want the cfg infrastructure in place but aren’t ready to gate the dep yet.