#[non_exhaustive]pub struct CodeGenConfig {Show 31 fields
pub generate_views: bool,
pub lazy_views: bool,
pub preserve_unknown_fields: bool,
pub generate_json: bool,
pub generate_arbitrary: bool,
pub extern_paths: Vec<(String, String)>,
pub bytes_fields: Vec<(String, BytesRepr)>,
pub string_fields: Vec<(String, StringRepr)>,
pub map_fields: Vec<(String, MapRepr)>,
pub pointer_fields: Vec<(String, PointerRepr)>,
pub repeated_fields: Vec<(String, RepeatedRepr)>,
pub unboxed_oneof_fields: Vec<String>,
pub strict_utf8_mapping: bool,
pub allow_message_set: bool,
pub generate_text: bool,
pub emit_register_fn: bool,
pub file_per_package: bool,
pub type_attributes: Vec<(String, String)>,
pub field_attributes: Vec<(String, String)>,
pub message_attributes: Vec<(String, String)>,
pub enum_attributes: Vec<(String, String)>,
pub oneof_attributes: Vec<(String, String)>,
pub gate_impls_on_crate_features: bool,
pub generate_with_setters: bool,
pub generate_reflection: bool,
pub generate_reflection_vtable: bool,
pub gate_reflect_on_crate_feature: bool,
pub idiomatic_enum_aliases: bool,
pub idiomatic_imports: bool,
pub feature_gate_names: FeatureGateNames,
pub type_name_prefix: String,
}Expand description
Configuration for code generation.
Fields (Non-exhaustive)§
This struct is marked as non-exhaustive
Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.generate_views: boolWhether to generate borrowed view types (MyMessageView<'a>) in
addition to owned types.
lazy_views: boolWhether to additionally generate the lazy view family
(MyMessageLazyView<'a>) alongside the eager views (default: false).
Lazy views implement buffa::LazyMessageView: decode_lazy performs
a single non-recursive scan, recording singular/repeated message
fields as undecoded byte ranges (LazyMessageFieldView /
LazyRepeatedView) that decode on access — reading a few fields of
many sub-messages no longer allocates or recurses into untouched
sub-trees. The eager MyMessageView family is unchanged (output is
byte-identical with or without this flag), so eager and lazy views
coexist and generic MessageView consumers never silently inherit
deferred validation.
Semantics of the lazy family:
- Eager carve-outs: groups / editions
DELIMITEDfields (no length prefix to defer), oneof message variants, and map message values use the eager view types. - Merge preserved: a singular message field split across wire occurrences is recorded as fragments and merged on access.
- Budgets flow: the recursion depth and unknown-field allowance remaining at each deferred field are recorded and replayed per access (a per-subtree approximation of the shared allowance).
- Deferred validation: malformed deferred bytes error on access,
from the fallible
to_owned_message, and as a serde error from the viewSerializeimpl.ViewEncodereplays recorded fragments without validating them. - No
ReflectMessage,OwnedView, or text-format surface — use the eager family for those.
Requires generate_views (the lazy family
reuses the eager view-oneof enums and eager sub-view types); with
views disabled the flag is ignored with a warning.
preserve_unknown_fields: boolWhether to preserve unknown fields (default: true).
generate_json: boolWhether to derive serde::Serialize / serde::Deserialize on
generated message structs and enum types, and emit #[serde(with = "...")]
attributes for proto3 JSON’s special scalar encodings (int64 as quoted
string, bytes as base64, etc.).
When this is true, the downstream crate must depend on serde and
must enable the buffa/json feature for the runtime helpers.
Oneof fields use #[serde(flatten)] with custom Serialize /
Deserialize impls so that each variant appears as a top-level
JSON field (proto3 JSON inline oneof encoding).
generate_arbitrary: boolWhether to emit #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
on generated message structs and enum types.
When this is true, the downstream crate must add arbitrary as an
optional dependency and enable the buffa/arbitrary feature. The
downstream crate’s Cargo feature that gates arbitrary must be named
exactly "arbitrary" — the generated cfg_attr uses that literal
string and cannot be customized. This applies to both the struct-level
derive(Arbitrary) and the per-field #[arbitrary(with = ...)]
attributes emitted for bytes_fields-typed fields.
For bytes_fields-typed fields, codegen emits #[arbitrary(with = ...)]
using helpers in ::buffa::__private since bytes::Bytes has no
Arbitrary impl. Singular, optional, and repeated bytes fields are all
covered. Map values are always Vec<u8> regardless of bytes_fields
and require no special handling.
extern_paths: Vec<(String, String)>External type path mappings.
Each entry maps either a fully-qualified protobuf package prefix
(e.g., ".my.common") to a Rust module path (e.g.,
"::common_protos"), or a single type FQN (e.g.,
".my.common.Shared") to a full Rust type path (e.g.,
"::shared_types::Shared"). Matched types reference the extern Rust
path instead of being generated, allowing shared proto packages to be
compiled once in a dedicated crate and referenced from others. An
exact type-FQN entry wins over a covering package prefix; otherwise
the longest matching prefix wins.
Well-known types (google.protobuf.*) are automatically mapped to
::buffa_types::google::protobuf::* without needing an explicit
entry here. To override with a custom implementation, add an
extern_path for .google.protobuf pointing to your crate.
bytes_fields: Vec<(String, BytesRepr)>Ordered (proto-path-prefix, BytesRepr) rules selecting the Rust type
for bytes fields. Later rules win, so a broad rule (e.g. "." →
Bytes) can be refined by a more specific one. Fields matching no rule
use Vec<u8>. The path is matched with the same proto-segment-aware
prefix logic as string_fields.
string_fields: Vec<(String, StringRepr)>Ordered (proto-path-prefix, StringRepr) rules selecting the Rust type
for string fields. Later rules win, so a broad rule (e.g. "." →
SmolStr) can be refined by a more specific one
(".my.pkg.Msg.field" → CompactString). Fields matching no rule use
String. The path is matched with the same proto-segment-aware prefix
logic as bytes_fields.
Applies to singular, optional, and repeated string fields and oneof
string variants. Map keys and values always stay String, mirroring
the bytes path (where map values always stay Vec<u8>).
map_fields: Vec<(String, MapRepr)>Ordered (proto-path-prefix, MapRepr) rules selecting the owned Rust
map collection for map fields. Later rules win, with the same
proto-segment-aware prefix matching as bytes_fields
("." matches every field). Fields matching no rule use HashMap<K, V>.
Independent of the element/value representation: a map field’s key and
value types are chosen by the usual scalar/string/bytes/message rules,
and this knob only changes the surrounding collection.
pointer_fields: Vec<(String, PointerRepr)>Ordered (proto-path-prefix, PointerRepr) rules selecting the owned
smart pointer for singular message fields (the pointer inside
MessageField<T>). Later rules win, same proto-segment-aware prefix
matching as bytes_fields. Fields matching no rule
use Box<T>.
Applies to singular (and proto2 optional/required) message fields only — not repeated message fields (a collection) or oneof message variants.
repeated_fields: Vec<(String, RepeatedRepr)>Ordered (proto-path-prefix, RepeatedRepr) rules selecting the owned
Rust collection for repeated fields. Later rules win, with the same
proto-segment-aware prefix matching as bytes_fields
("." matches every field). Fields matching no rule use Vec<T>.
Applies only to repeated fields (not map, whose collection stays
the configured map type). The element type is chosen by the usual
scalar/string/bytes/message rules and substituted into the collection
template.
unboxed_oneof_fields: Vec<String>Fully-qualified proto paths whose message-typed oneof variants should
not be wrapped in Box<T>. By default every message/group oneof
variant is boxed (so recursive types compile); entries here opt matching
variants out, storing the message inline in the enum.
Each entry is a proto path prefix matched with the same
proto-segment-aware logic as bytes_fields
("." matches every variant). Recursive variants cannot be stored
inline (the type would be unsized): an entry naming one exactly is
rejected at codegen time, while a broader prefix entry silently keeps
recursive variants boxed and inlines the rest.
strict_utf8_mapping: boolHonor features.utf8_validation = NONE by emitting Vec<u8> / &[u8]
for such string fields instead of String / &str.
When false (the default), buffa emits String for all string fields
and validates UTF-8 on decode — stricter than proto2 requires, but
ergonomic and safe.
When true, string fields with utf8_validation = NONE (all proto2
strings by default, and editions fields that opt into NONE) become
Vec<u8> / &[u8]. Decode skips validation; the caller decides at the
call site whether to std::str::from_utf8 (checked) or
from_utf8_unchecked (trusted-input fast path). This is the only
sound Rust mapping when strings may actually contain non-UTF-8 bytes.
This is a breaking change for proto2 — enable only for new code or when profiling identifies UTF-8 validation as a bottleneck.
allow_message_set: boolPermit option message_set_wire_format = true on input messages.
MessageSet is a legacy Google-internal wire format that wraps each
extension in a group structure instead of using regular field tags.
When false (the default), encountering such a message is a codegen
error — the flag exists to make MessageSet use explicit, since the
format is obsolete outside of interop with very old Google protos.
generate_text: boolWhether to emit impl buffa::text::TextFormat on generated message
structs for textproto (human-readable text format) encoding/decoding.
When this is true, the downstream crate must enable the buffa/text
feature for the runtime encoder/decoder.
emit_register_fn: boolWhether the per-package .mod.rs stitcher emits
__buffa::register_types(&mut TypeRegistry).
Default true. The fn aggregates Any type entries and extension
entries for every message in the package. Set to false for
crates that don’t use extensions/Any, or that hand-roll
registration (e.g. buffa-types’ register_wkt_types, which
knows the JSON-Any is_wkt special-casing the generic fn does
not). The per-message __*_JSON_ANY / __*_TEXT_ANY consts are
still emitted; only the aggregating fn is suppressed.
file_per_package: boolEmit one <dotted.package>.rs per proto package instead of the
per-proto-file content set plus <pkg>.mod.rs stitcher.
The single file inlines what the stitcher would otherwise include!,
producing the same __buffa::{view,oneof,ext,...} module structure.
Intended for Buf Schema Registry generated SDKs, whose lib.rs
synthesis builds the module tree from <dotted.package>.rs filenames.
Under strategy: directory this only sees one directory’s files per
invocation, so the input module must be PACKAGE_DIRECTORY_MATCH-clean
(one package per directory) for the output to be complete. BSR-hosted
modules satisfy this by lint default. If a package spans multiple
directories, separate invocations each emit their own <pkg>.rs and
the last write wins — silent partial output, not a codegen error.
type_attributes: Vec<(String, String)>Custom attributes to inject on generated types (messages, enums, and
oneof enums — the latter matched on the oneof’s own path,
.my.pkg.MyMessage.my_oneof).
Each entry is (proto_path, attribute). The proto_path is matched
as a prefix against the fully-qualified proto name: "." applies to
all types, ".my.pkg" to types in that package, ".my.pkg.MyMessage"
to a specific type. The attribute is a raw Rust attribute string
(e.g., "#[derive(serde::Serialize)]").
field_attributes: Vec<(String, String)>Custom attributes to inject on generated struct fields.
Each entry is (proto_path, attribute). The proto_path is matched
as a prefix against the fully-qualified field path (e.g.,
".my.pkg.MyMessage.my_field"). "." applies to all fields.
message_attributes: Vec<(String, String)>Custom attributes to inject on generated message structs only (not enums).
Same path-matching semantics as type_attributes, but only applied to
message structs, not enum types. Useful for struct-only attributes like
#[serde(default)].
enum_attributes: Vec<(String, String)>Custom attributes to inject on generated enum types only (not messages).
Same path-matching semantics as type_attributes, but only applied to
enum types. Useful for enum-only attributes like
#[derive(strum::EnumIter)] when the user does not want to apply the
same attribute to every message in the matched scope.
oneof_attributes: Vec<(String, String)>Custom attributes to inject on generated oneof enums only (not messages, not regular enums).
Same path-matching semantics as type_attributes, matched against the
oneof’s fully-qualified path (.pkg.Message.oneof_name). Useful when a
oneof needs a different attribute set than the surrounding types — e.g.
keeping #[derive(serde::Serialize)] on messages and oneofs while a
separate enum_attributes entry puts a different serde derive on the
regular enums.
gate_impls_on_crate_features: boolWrap generated impls in #[cfg(feature = "...")] instead of
emitting them unconditionally.
When true, the impls controlled by generate_json,
generate_views, and generate_text are emitted wrapped in
#[cfg(feature = "json" | "views" | "text")] (or
#[cfg_attr(feature = ..., ...)] for derives and field attributes)
rather than unconditionally. The consuming crate must define matching
Cargo features that enable the corresponding runtime support, e.g.:
[features]
json = ["buffa/json", "dep:serde", "dep:serde_json"]
views = []
text = ["buffa/text"]The generate_* flags still control whether an impl kind is
emitted at all — this flag only controls whether it is cfg-gated.
generate_arbitrary is always cfg_attr-gated on
feature = "arbitrary" regardless of this flag, because arbitrary
is an optional dependency by design.
When generate_reflection is also on, the
reflection impls are gated on feature = "reflect" alongside
json/views/text. To gate only reflection without gating json/views/text,
use gate_reflect_on_crate_feature
instead.
This is the mechanism that lets buffa-descriptor and buffa-types
ship every impl while keeping the codegen toolchain
(buffa-codegen/buffa-build/protoc-gen-buffa) lean: those crates
depend on buffa-descriptor with default-features = false and so
don’t pull serde/serde_json/base64. Most consumers don’t need
this — they decide at build-script time whether to generate JSON, and
if they say yes, they want impl Serialize to just exist.
generate_with_setters: boolGenerate with_* builder-style setter methods for explicit-presence fields.
Each explicit-presence scalar, bytes, or enum field gets a
pub fn with_<name>(mut self, value: T) -> Self method that wraps the
value in Some and returns self, enabling chained construction:
let req = MyRequest::default()
.with_name("alice")
.with_timeout_ms(30_000);Fields that receive a setter: proto3 optional, proto2 optional,
and editions fields with field_presence = EXPLICIT.
Fields that do not receive a setter: message fields
(MessageField<T>), repeated fields, map fields, oneof variant fields,
proto2 required fields, and any implicit-presence field.
There is no clear_<name> companion — to clear a field, assign None
directly: msg.name = None;.
Defaults to true.
generate_reflection: boolGenerate impl Reflectable for owned message types (bridge mode).
When enabled, each generated message gets an
impl ::buffa_descriptor::reflect::Reflectable whose reflect()
round-trips through DynamicMessage (encode → decode → reflective
handle), and the package’s __buffa::reflect submodule embeds the
FileDescriptorSet bytes plus a lazily-built DescriptorPool.
Runtime requirements — the consuming crate must depend on:
buffa-descriptorwith thereflectfeature.std(the lazy pool accessor usesstd::sync::OnceLock).
When gate_impls_on_crate_features
is on, the impls are wrapped in #[cfg(feature = "reflect")] so the
consuming crate can opt out per build.
Performance — reflect() is one full encode/decode round-trip
plus a heap allocation. The first call also pays a one-time pool
build cost (linking the embedded FileDescriptorSet). For zero-copy
reflective access over view types without the round-trip, additionally
enable generate_reflection_vtable.
Binary size — each package embeds its own copy of the full
FileDescriptorSet (transitive closure). For a multi-package
codegen run this duplicates the FDS bytes per package. Acceptable
for the bridge prototype; deduplication via a crate-root module is
a planned follow-up.
Defaults to false.
generate_reflection_vtable: boolEmit vtable-mode reflection: impl ReflectMessage / impl ReflectElement on the owned message structs and (when views are
generated) the view types, and switch the owned
Reflectable::reflect() body to borrow self
(ReflectCow::Borrowed(self)) instead of the bridge round-trip.
Reflective access then reads struct fields in place — no encode/decode round-trip and no per-field allocation — for both a decoded view and an in-memory owned message.
Requires generate_reflection (the impls
resolve against the same embedded DescriptorPool) but not
generate_views — with views off, only the
owned impls are emitted. Set via ReflectMode::VTable
— front-ends expose it as buffa_build::Config::reflect_mode /
protoc-gen-buffa’s reflect_mode=vtable.
Defaults to false.
gate_reflect_on_crate_feature: boolGate the reflection impls behind a reflect crate feature, without
gating json/views/text (unlike
gate_impls_on_crate_features,
which gates them all together).
Used by crates that ship view/text impls unconditionally but want the
reflection surface — which pulls a buffa-descriptor dependency and
std — to be opt-in. buffa-types is the motivating case: its WKT
views are always available, but impl ReflectMessage for them is gated
behind buffa-types’s reflect feature.
When gate_impls_on_crate_features
is already on, reflection is gated regardless and this flag is ignored.
A low-level knob for crates whose generated code is a public interface
(buffa-types, the conformance harness). Set directly by gen_wkt_types
and exposed through buffa_build::Config::gate_reflect_on_crate_feature
(currently #[doc(hidden)], paired with the experimental vtable flag).
Defaults to false.
idiomatic_enum_aliases: boolEmit idiomatic UpperCamelCase constant aliases alongside each enum
variant.
Protobuf style names enum values in SHOUTY_SNAKE_CASE, conventionally
prefixed with the enum name (RULE_LEVEL_HIGH). Those names remain the
definitive Rust variants — they are guaranteed unique and valid by
protobuf, and existing references (including Debug output) are
unchanged. When this is enabled, codegen additionally emits associated
consts with the prefix stripped and the name converted to
UpperCamelCase (RULE_LEVEL_HIGH → High), so downstream code can
write RuleLevel::High.
The conversion is lossy, so two values can collide (FOO_BAR and
FOO__BAR both map to FooBar). The rule is all-or-nothing per enum:
if any two values would collide after conversion, or a value would yield
an invalid identifier, no aliases are emitted for that enum (a
CodeGenWarning and an enum doc note explain why). This keeps every
match either fully SHOUTY_SNAKE_CASE or fully idiomatic, never a forced
mix.
The aliases are associated consts, which work in pattern position too:
a match written entirely against aliases is still exhaustiveness-checked
(the “non-exhaustive” error names the underlying SHOUTY_SNAKE_CASE
variant, since that is the canonical name).
Defaults to true: the aliases are purely additive (the proto names
remain the variants, and Debug is unchanged), so enabling by default is
backward-compatible, and the all-or-nothing rule guarantees correctness on
any enum.
idiomatic_imports: boolEmit use-backed short type names at the package root instead of
fully-qualified paths, so generated code reads like hand-written
Rust (pub at: MessageField<Timestamp> instead of
pub at: ::buffa::MessageField<::buffa_types::google::protobuf::Timestamp>).
Requires file_per_package: only there is
the package-root scope a single-writer file whose complete name set
is known at generation time. In the multi-file layout the stitcher
include!-merges every proto’s content files into the shared root
scope, where emitted use directives could collide across files —
generate returns an error for that combination rather than
silently ignoring the flag.
Off by default; default output is byte-for-byte unchanged. Short
names are always backed by an explicit use (never glob reliance),
are refused when they would collide with the package’s own items or
names referenced bare by sibling emissions, and fall back to
parent-module qualification and then the fully-qualified path. The
short-name assignment (use block and per-path choices) is computed
from a collection pre-pass and is stable under .proto file
reordering; item order within the file still follows input order,
so whole-file output is not reorder-invariant. The pre-pass
generates the package twice, roughly doubling codegen time for it.
Scope: only package-root type declarations (struct fields, oneof
Option wrappers) are shortened. Impl bodies, nested-message
modules, and __buffa internals keep fully-qualified paths — the
readability payoff lands where consumers look (struct definitions
and rustdoc), not in the codec internals.
Experimental means: the generated-output shape may change between releases (requiring regeneration of checked-in code), and the option itself may be renamed or removed outside semver guarantees.
feature_gate_names: FeatureGateNamesCrate feature names used by the #[cfg(feature = "...")] gates that
gate_impls_on_crate_features
and
gate_reflect_on_crate_feature
emit.
Defaults to "json" / "views" / "text" / "reflect". Override a
name when the consuming crate gates the same concern behind a
different feature name (e.g. its JSON support behind a serde
feature). Inert unless one of the gating flags is on.
type_name_prefix: StringPrefix prepended to every locally-generated Rust type name.
With prefix "Rpc", message User {} generates struct RpcUser,
its view becomes RpcUserView / RpcUserOwnedView, and every
cross-reference (fields, oneof variants, maps, extensions) uses the
prefixed name. Useful in multi-protocol systems where generated
types from different domains would otherwise collide with each
other or with a canonical hand-written model.
The prefix applies to message structs and enum types (top-level and nested, plus their derived view/owned-view types). It does not apply to:
- module names (
message Outerstill nests underpub mod outer— modules are namespaced by the package tree and never collide with type names), - oneof enums (structurally namespaced under
__buffa::oneof::, named after the oneof declaration, not the message), - types mapped away via
extern_pathsor the automatic well-known-type mapping (their names are owned by the external crate), - wire-format and JSON output (proto names,
TYPE_URLs, and JSON field names are unaffected — this is a pure Rust-identifier rename).
When another codegen run references these prefixed types via its own
extern_paths mapping, the mapped Rust path
must spell out the prefixed name (e.g. ::crate_a::RpcUser) — the
proto name carries no prefix, so the mapping is not derived
automatically. Prefix-induced name collisions (e.g. message RpcUser
alongside message User with prefix Rpc) are not detected here;
they surface as ordinary duplicate-definition errors when the
generated code is compiled.
Must be PascalCase ([A-Z][A-Za-z0-9]*) — an ASCII uppercase letter
followed by ASCII letters and digits — so the prefixed names stay
conventionally cased; generation fails with
CodeGenError::InvalidTypeNamePrefix otherwise. Defaults to ""
(no prefix).
Trait Implementations§
Source§impl Clone for CodeGenConfig
impl Clone for CodeGenConfig
Source§fn clone(&self) -> CodeGenConfig
fn clone(&self) -> CodeGenConfig
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more