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. Under encodable_impls=all_messages, also per proto whose messages yield at least one Encodable impl pair. 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.8.0 with views enabled — the stubs use the buffa::HasMessageView impls and owned-view wrappers generated with each message (buffa-types 0.8+ 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, for proto-only builds. Pair it with connectrpc’s default-features = false (the json cargo feature off) so the runtime drops its matching serde bounds. 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")].
  • gate_client_feature=<name> — same gate, but use <name> as the Cargo feature instead of client.
  • encodable_impls=all_messages — emit the ::connectrpc::Encodable view impl pair for every message defined in each targeted proto, not only for RPC output types, and emit a companion file even for protos that declare no services. Use this in the generation run of a crate that owns message types consumed by other crates’ services (a shared proto crate in a multi-crate split): Rust’s orphan rules require these impls to live in the crate that defines the view types, so downstream service crates skip them and their handlers would otherwise have to return owned messages or PreEncoded::from_view. (View bodies serve the proto codec only — JSON-codec requests get Unimplemented, as for any view body.) The emitting crate must depend on connectrpc, and the companion files for service-less packages must be mounted like any other plugin output — an unmounted companion surfaces as a missing Encodable impl in the consuming crate. Messages mapped to a foreign crate via extern_path are still skipped. encodable_impls=outputs is the explicit default. See Options::encodable_impls.

§Client-side cfg gate

When gate_client_feature is set, the consumer crate must declare the named Cargo feature (client by default). 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.