//! Generate field-wise typed messages (not DynamicMessage wrappers, not upb).
#![allow(
clippy::disallowed_methods,
clippy::expect_used,
reason = "protoc plugin / compile_protos is sync build-time IO, not an async worker"
)]
use crate::dynamic::{
Cardinality, DescriptorPool, FieldDescriptor, FieldType, MessageDescriptor, MethodDescriptor,
Presence, ServiceDescriptor,
};
use crate::error::ParseError;
use crate::wire::{self, decode_tag, encode_len_field, encode_varint, read_len_bytes, WIRE_LEN};
use std::cell::{Cell, RefCell};
use std::fmt::Write as _;
use std::path::{Path, PathBuf};
use std::process::Command;
thread_local! {
static IDENTS: RefCell<std::collections::BTreeMap<String, String>> =
const { RefCell::new(std::collections::BTreeMap::new()) };
static FIELD_IDENTS: RefCell<std::collections::BTreeMap<u32, String>> =
const { RefCell::new(std::collections::BTreeMap::new()) };
static FIELD_RAWS: RefCell<std::collections::BTreeMap<u32, String>> =
const { RefCell::new(std::collections::BTreeMap::new()) };
static STUBS: Cell<Stubs> = const { Cell::new(Stubs::Kernel) };
static EMIT_DEPS: Cell<bool> = const { Cell::new(false) };
}
/// Which gRPC service stubs to emit alongside generated messages.
///
/// A `.proto` `service` block turns into client and server types. Which flavour
/// you get depends on which gRPC stack you are using; the two are mutually
/// exclusive because they claim the same `FooClient` / `FooServer` names.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum Stubs {
/// Messages only.
None,
/// `tonic` 0.14 stubs over `protobuf_tonic::ProtobufCodec`.
/// Select with [`Config::emit_tonic_stubs`].
Tonic,
/// Native `pbrs-grpc` stubs. The default. Requires the generating crate
/// to depend on `pbrs-grpc`. `FooClient` dials with `connect` /
/// `connect_tls` / `connect_unix` / `from_io`; `FooServer` serves with
/// `serve` / `serve_tls` / `serve_unix`.
/// The `protoc-gen-pbrs` plugin uses this default; set
/// `PURE_PROTOBUF_STUBS=tonic` for tonic stubs.
#[default]
Kernel,
}
/// Resolve the stub flavour from the thread-local config, then the plugin
/// environment.
fn stubs_setting() -> Stubs {
match std::env::var("PURE_PROTOBUF_STUBS").as_deref() {
Ok("kernel") => Stubs::Kernel,
Ok("tonic") => Stubs::Tonic,
Ok("none") => Stubs::None,
_ => STUBS.with(Cell::get),
}
}
pub fn generate_from_code_generator_request(
bytes: &[u8],
) -> Result<Vec<(String, String)>, ParseError> {
let mut files_to_generate = Vec::new();
let mut proto_files = Vec::new();
let mut pos = 0;
while pos < bytes.len() {
let (n, w) = decode_tag(bytes, &mut pos)?;
match (n, w) {
(1, WIRE_LEN) => {
let s = read_len_bytes(bytes, &mut pos)?;
files_to_generate.push(String::from_utf8_lossy(s).into_owned());
}
(15, WIRE_LEN) => {
let blob = read_len_bytes(bytes, &mut pos)?.to_vec();
proto_files.push(blob);
}
_ => wire::skip_field(bytes, &mut pos, w)?,
}
}
let mut fds = Vec::new();
for blob in &proto_files {
encode_len_field(&mut fds, 1, blob);
}
let pool = DescriptorPool::from_file_descriptor_set(&fds)?;
let names: Vec<String> = pool.collect_names();
let targets = if files_to_generate.is_empty() {
vec!["generated.proto".into()]
} else {
files_to_generate.clone()
};
let mut out_files = Vec::new();
for target in &targets {
let wanted: std::collections::BTreeSet<String> = std::iter::once(target.clone()).collect();
let target_is_wkt = wanted.iter().any(|w| {
let s = w.replace('\\', "/");
s.contains("google/protobuf/") && !s.contains("test_messages")
});
let mut src = String::from(
"// @generated by protoc-gen-pbrs\n#[allow(unused, non_snake_case, non_camel_case_types, clippy::all, clippy::restriction, clippy::indexing_slicing, clippy::cast_possible_truncation, clippy::cast_possible_wrap, clippy::cast_sign_loss, clippy::let_underscore_must_use, unreachable_pub, reason = \"generated by protoc-gen-pbrs\")]\nmod __gen {\n#![allow(unused, non_snake_case, non_camel_case_types, clippy::all, clippy::restriction, clippy::indexing_slicing, clippy::cast_possible_truncation, clippy::cast_possible_wrap, clippy::cast_sign_loss, clippy::let_underscore_must_use, unsafe_code, unreachable_pub, reason = \"generated by protoc-gen-pbrs\")]\nuse pbrs::prelude::*;\nuse pbrs::{Enum, Map, MapMut, MapView, ParseError, ProtoBytes, ProtoString, Repeated, RepeatedMut, RepeatedView, SerializeError, UnknownEnumValue};\nuse pbrs::UnknownFields;\n\n",
);
let no_reflect = std::env::var("PURE_PROTOBUF_NO_REFLECT").as_deref() == Ok("1");
if no_reflect {
// Accessor/binary tests: skip FileDescriptorSet hex and JSON/text.
} else if std::env::var("PURE_PROTOBUF_SHARED_POOL").as_deref() == Ok("1") {
src.push_str(
"fn generated_pool() -> std::sync::Arc<pbrs::DescriptorPool> {\n pbrs::gencode::conformance_pool()\n}\n\n",
);
} else {
emit_fds(&mut src, &fds);
src.push_str(
"fn generated_pool() -> std::sync::Arc<pbrs::DescriptorPool> {\n static P: std::sync::OnceLock<std::sync::Arc<pbrs::DescriptorPool>> = std::sync::OnceLock::new();\n P.get_or_init(|| {\n std::sync::Arc::new(pbrs::DescriptorPool::from_file_descriptor_set(FILE_DESCRIPTOR_SET).expect(\"fds\"))\n }).clone()\n}\n\n",
);
}
let mut emit_names = Vec::new();
for name in &names {
let Some(desc) = pool.get_message(name) else {
continue;
};
if desc.is_map_entry {
continue;
}
let wkt = desc.full_name.starts_with("google.protobuf.");
let emit_wkt = wkt && !target_is_wkt && std::env::var("PURE_PROTOBUF_NO_WKT").is_err();
let emit_deps = EMIT_DEPS.with(Cell::get)
|| std::env::var("PURE_PROTOBUF_EMIT_DEPS").as_deref() == Ok("1");
if !file_matches(&wanted, &desc.file_name) && !emit_wkt && !(emit_deps && !wkt) {
continue;
}
emit_names.push(name.clone());
}
let pub_files = pool.public_import_files(&targets);
emit_names.retain(|n| {
pool.get_message(n).is_none_or(|d| {
!pub_files
.iter()
.any(|p| file_matches(&std::iter::once(p.clone()).collect(), &d.file_name))
})
});
let mut emit_enums = Vec::new();
for name in pool.collect_enum_names() {
let Some(ed) = pool.get_enum(&name) else {
continue;
};
let wkt = ed.full_name.starts_with("google.protobuf.");
let emit_wkt = wkt && !target_is_wkt && std::env::var("PURE_PROTOBUF_NO_WKT").is_err();
let emit_deps = EMIT_DEPS.with(Cell::get)
|| std::env::var("PURE_PROTOBUF_EMIT_DEPS").as_deref() == Ok("1");
if !file_matches(&wanted, &ed.file_name) && !emit_wkt && !(emit_deps && !wkt) {
continue;
}
emit_enums.push(name);
}
emit_enums.retain(|n| {
pool.get_enum(n).is_none_or(|d| {
!pub_files
.iter()
.any(|p| file_matches(&std::iter::once(p.clone()).collect(), &d.file_name))
})
});
let mut ident_names = emit_names.clone();
ident_names.extend(emit_enums.iter().cloned());
let msg_set: std::collections::BTreeSet<String> =
pool.collect_names().into_iter().collect();
IDENTS.with(|c| *c.borrow_mut() = unique_idents(&ident_names, &msg_set));
emit_public_uses(&mut src, &pool, &pub_files);
for name in &emit_enums {
let ed = pool.get_enum(name).expect("emit enum");
emit_enum(&mut src, &ed);
}
for name in &emit_names {
let desc = pool.get_message(name).expect("emit");
emit_message(&mut src, &desc);
emit_map_decoders(&mut src, &desc);
}
emit_nested_mods(&mut src, &emit_names, &emit_enums);
let services: Vec<_> = pool
.collect_services()
.into_iter()
.filter(|s| file_matches(&wanted, &s.file_name))
.collect();
match stubs_setting() {
Stubs::None => {}
Stubs::Tonic if !services.is_empty() => {
src.push_str("\n// --- gRPC stubs (protobuf-tonic, not tonic-prost) ---\n");
src.push_str("use protobuf_tonic::ProtobufCodec;\n");
src.push_str("use std::convert::Infallible;\n");
src.push_str("use std::future::Future;\n");
src.push_str("use std::pin::Pin;\n");
src.push_str("use std::sync::Arc;\n");
src.push_str("use std::task::{Context, Poll};\n");
for svc in &services {
emit_service(&mut src, svc);
}
}
Stubs::Kernel if !services.is_empty() => {
src.push_str("\n// --- gRPC stubs (pbrs-grpc kernel) ---\n");
for svc in &services {
emit_kernel_service(&mut src, svc);
}
}
Stubs::Tonic | Stubs::Kernel => {}
}
src.push_str("}\n#[allow(unused_imports, reason = \"generated by protoc-gen-pbrs\")]\npub use __gen::*;\n");
let stem = std::path::Path::new(target)
.file_stem()
.and_then(|s| s.to_str())
.unwrap_or("generated");
out_files.push((format!("{stem}.rs"), src));
}
Ok(out_files)
}
/// Generate from a FileDescriptorSet plus the proto paths to emit.
pub fn generate_from_file_descriptor_set(
fds: &[u8],
files_to_generate: &[String],
) -> Result<Vec<(String, String)>, ParseError> {
let mut req = Vec::new();
for f in files_to_generate {
encode_string_field(&mut req, 1, f);
}
let mut pos = 0;
while pos < fds.len() {
let (n, w) = decode_tag(fds, &mut pos)?;
if n == 1 && w == WIRE_LEN {
let blob = read_len_bytes(fds, &mut pos)?;
encode_len_field(&mut req, 15, blob);
} else {
wire::skip_field(fds, &mut pos, w)?;
}
}
generate_from_code_generator_request(&req)
}
/// prost-build-shaped compile: `protoc` FileDescriptorSet, then gencode into
/// `OUT_DIR` (or [`Config::out_dir`]).
pub fn compile_protos(
protos: &[impl AsRef<Path>],
includes: &[impl AsRef<Path>],
) -> Result<(), ParseError> {
Config::new().compile_protos(protos, includes)
}
/// Options for [`compile_protos`].
#[derive(Default)]
pub struct Config {
out_dir: Option<PathBuf>,
stubs: Stubs,
emit_deps: bool,
}
impl Config {
pub fn new() -> Self {
Self::default()
}
pub fn out_dir(&mut self, path: impl Into<PathBuf>) -> &mut Self {
self.out_dir = Some(path.into());
self
}
/// Choose which gRPC stub flavour to emit. Default [`Stubs::Kernel`].
pub fn stubs(&mut self, stubs: Stubs) -> &mut Self {
self.stubs = stubs;
self
}
/// Emit tonic `FooClient`/`FooServer` stubs.
///
/// Needed because the default is [`Stubs::Kernel`]. `false` means
/// messages only. Mutually exclusive with [`Self::emit_kernel_stubs`];
/// the last call wins.
pub fn emit_tonic_stubs(&mut self, enable: bool) -> &mut Self {
self.stubs = if enable { Stubs::Tonic } else { Stubs::None };
self
}
/// Emit native `pbrs-grpc` `FooClient`/`FooServer` stubs.
///
/// This is the default for [`Config::new`] / [`compile_protos`]. Pass
/// `true` to be explicit; `false` is messages only. The generating crate
/// must depend on `pbrs-grpc`. `FooClient` gets the same dialers as
/// `Channel` (`connect`, `connect_tls`, `connect_unix`, `from_io`, and
/// the lazy/`_with` variants) and the same overlays, including
/// `https_scheme` for already-encrypted `from_io` streams and `origin`
/// for `:authority`. Read those
/// overlays with `scheme` / `authority` / `grpc_user_agent` /
/// `rpc_timeout` / `waits_for_ready` / `compresses_outbound` /
/// `gzip_level` / `accepts_compressed` / `concurrent_rpc_limit` / `stream_buffer_size` / `send_buffer_size` / `limits` / `config`.
/// Methods you omit on the generated `Foo` trait answer `UNIMPLEMENTED`.
/// Mutually exclusive with [`Self::emit_tonic_stubs`]; the last call wins.
///
/// ```no_run
/// // build.rs
/// pbrs::codegen::compile_protos(&["proto/hello.proto"], &["proto"])
/// .expect("codegen");
/// ```
pub fn emit_kernel_stubs(&mut self, enable: bool) -> &mut Self {
self.stubs = if enable { Stubs::Kernel } else { Stubs::None };
self
}
/// Emit imported non-WKT messages into the same generated file.
///
/// Needed for `grpc.testing.test.proto`, which imports
/// `messages.proto` / `empty.proto`. `PURE_PROTOBUF_EMIT_DEPS=1`
/// remains the plugin/env equivalent.
pub fn emit_deps(&mut self, enable: bool) -> &mut Self {
self.emit_deps = enable;
self
}
pub fn compile_protos(
&self,
protos: &[impl AsRef<Path>],
includes: &[impl AsRef<Path>],
) -> Result<(), ParseError> {
let out = match &self.out_dir {
Some(p) => p.clone(),
None => PathBuf::from(
std::env::var("OUT_DIR").map_err(|_| ParseError::new("missing OUT_DIR"))?,
),
};
std::fs::create_dir_all(&out).map_err(|e| ParseError::owned(e.to_string()))?;
for p in protos {
println!("cargo:rerun-if-changed={}", p.as_ref().display());
}
let fds_path = out.join("pbrs.fds");
let mut cmd = Command::new("protoc");
cmd.arg("--include_imports")
.arg(format!("--descriptor_set_out={}", fds_path.display()));
for inc in includes {
cmd.arg("-I").arg(inc.as_ref());
}
for p in protos {
cmd.arg(p.as_ref());
}
let status = cmd.status().map_err(|e| ParseError::owned(e.to_string()))?;
if !status.success() {
return Err(ParseError::owned(format!("protoc failed: {status}")));
}
let bytes = std::fs::read(&fds_path).map_err(|e| ParseError::owned(e.to_string()))?;
let names: Vec<String> = protos
.iter()
.map(|p| {
p.as_ref()
.file_name()
.and_then(|s| s.to_str())
.unwrap_or("generated.proto")
.to_string()
})
.collect();
STUBS.with(|c| c.set(self.stubs));
EMIT_DEPS.with(|c| c.set(self.emit_deps));
let files = generate_from_file_descriptor_set(&bytes, &names);
STUBS.with(|c| c.set(Stubs::default()));
EMIT_DEPS.with(|c| c.set(false));
let files = files?;
for (name, src) in files {
std::fs::write(out.join(name), src).map_err(|e| ParseError::owned(e.to_string()))?;
}
Ok(())
}
}
pub fn encode_code_generator_response(files: &[(String, String)]) -> Vec<u8> {
let mut out = Vec::new();
// PROTO3_OPTIONAL | SUPPORTS_EDITIONS
encode_varint_field(&mut out, 2, 3);
encode_varint_field(&mut out, 3, 998); // EDITION_PROTO2
encode_varint_field(&mut out, 4, 1000); // EDITION_2023
for (name, content) in files {
let mut file = Vec::new();
encode_string_field(&mut file, 1, name);
encode_string_field(&mut file, 15, content);
encode_len_field(&mut out, 15, &file);
}
out
}
fn file_matches(wanted: &std::collections::BTreeSet<String>, file_name: &str) -> bool {
if file_name.is_empty() {
return true;
}
let file_stem = std::path::Path::new(file_name)
.file_stem()
.and_then(|s| s.to_str())
.unwrap_or(file_name);
wanted.iter().any(|w| {
let w_stem = std::path::Path::new(w)
.file_stem()
.and_then(|s| s.to_str())
.unwrap_or(w);
w == file_name || w.ends_with(file_name) || file_name.ends_with(w) || w_stem == file_stem
})
}
fn emit_fds(src: &mut String, fds: &[u8]) {
src.push_str("pub const FILE_DESCRIPTOR_SET: &[u8] = &[\n");
for (i, b) in fds.iter().enumerate() {
if i % 16 == 0 {
src.push_str(" ");
}
let _ = write!(src, "0x{b:02x},");
if i % 16 == 15 {
src.push('\n');
}
}
if fds.len() % 16 != 0 {
src.push('\n');
}
src.push_str("];\n\n");
}
fn encode_varint_field(out: &mut Vec<u8>, n: u32, v: u64) {
crate::wire::encode_tag(out, n, crate::wire::WIRE_VARINT);
encode_varint(out, v);
}
fn encode_string_field(out: &mut Vec<u8>, n: u32, s: &str) {
encode_len_field(out, n, s.as_bytes());
}
fn ident_last(s: &str) -> String {
let key = s.trim_start_matches('.');
let last = key.rsplit('.').next().unwrap_or(key);
rust_ident_from_last(last)
}
fn must_mangle_view_suffix(full: &str, messages: &std::collections::BTreeSet<String>) -> bool {
let key = full.trim_start_matches('.');
let last = key.rsplit('.').next().unwrap_or(key);
let Some(without) = last.strip_suffix("View") else {
return false;
};
if without.is_empty() {
return false;
}
let sibling = match key.rsplit_once('.') {
Some((parent, _)) => format!("{parent}.{without}"),
None => without.to_string(),
};
messages.contains(&sibling)
}
fn file_stem_ident(path: &str) -> String {
std::path::Path::new(path)
.file_stem()
.and_then(|s| s.to_str())
.unwrap_or(path)
.replace('-', "_")
}
fn emit_public_uses(src: &mut String, pool: &DescriptorPool, pub_files: &[String]) {
for p in pub_files {
let stem = file_stem_ident(p);
let _ = writeln!(src, "pub use crate::{stem}::*;");
}
let _ = pool;
}
fn field_raw(f: &FieldDescriptor) -> String {
FIELD_RAWS.with(|c| {
c.borrow()
.get(&f.number)
.cloned()
.unwrap_or_else(|| f.name.clone())
})
}
fn unique_idents(
full_names: &[String],
messages: &std::collections::BTreeSet<String>,
) -> std::collections::BTreeMap<String, String> {
let mut used: std::collections::BTreeSet<String> = std::collections::BTreeSet::new();
let mut out = std::collections::BTreeMap::new();
for full in full_names {
let mut last = ident_last(full);
if must_mangle_view_suffix(full, messages) {
last.push('_');
}
let id = if used.contains(&last) {
let parent = full.rsplit_once('.').map(|x| x.0).unwrap_or("X");
format!("{}{last}", ident_last(parent))
} else {
last
};
used.insert(id.clone());
out.insert(full.clone(), id);
}
out
}
fn rust_ident(s: &str) -> String {
let key = s.trim_start_matches('.');
if let Some(id) = IDENTS.with(|c| c.borrow().get(key).cloned()) {
return id;
}
ident_last(key)
}
fn is_rust_keyword(id: &str) -> bool {
matches!(
id,
"as" | "async"
| "await"
| "break"
| "const"
| "continue"
| "crate"
| "dyn"
| "else"
| "enum"
| "extern"
| "false"
| "fn"
| "for"
| "if"
| "impl"
| "in"
| "let"
| "loop"
| "match"
| "mod"
| "move"
| "mut"
| "pub"
| "ref"
| "return"
| "self"
| "Self"
| "static"
| "struct"
| "super"
| "trait"
| "true"
| "type"
| "union"
| "unsafe"
| "use"
| "where"
| "while"
| "abstract"
| "become"
| "box"
| "do"
| "final"
| "macro"
| "override"
| "priv"
| "try"
| "typeof"
| "unsized"
| "virtual"
| "yield"
| "gen"
| "unknown"
)
}
fn field_ident_base(name: &str) -> String {
let mut id = String::new();
for (i, c) in name.chars().enumerate() {
if c.is_ascii_alphanumeric() || c == '_' {
id.push(c);
} else if i > 0 {
id.push('_');
}
}
if id.is_empty() {
id.push('_');
}
if id.chars().next().is_some_and(|c| c.is_ascii_digit()) {
id = format!("f_{id}");
}
if matches!(
id.as_str(),
"crate" | "self" | "Self" | "super" | "_" | "new"
) {
format!("{id}_")
} else if is_rust_keyword(&id) {
format!("r#{id}")
} else {
id
}
}
fn field_name_with_collision_avoidance(desc: &MessageDescriptor, f: &FieldDescriptor) -> String {
let name = f.name.as_str();
for prefix in ["clear_", "has_", "set_"] {
if let Some(rest) = name.strip_prefix(prefix) {
if desc.fields.values().any(|o| o.name == rest) {
return format!("{name}_{}", f.number);
}
}
}
for suffix in ["_mut", "_opt"] {
if let Some(rest) = name.strip_suffix(suffix) {
if desc.fields.values().any(|o| o.name == rest) {
return format!("{name}_{}", f.number);
}
}
}
name.to_string()
}
fn bind_field_idents(desc: &MessageDescriptor) {
let mut used = std::collections::BTreeSet::new();
used.insert("unknown".into());
let mut raw_used = std::collections::BTreeSet::new();
let mut out = std::collections::BTreeMap::new();
let mut raws = std::collections::BTreeMap::new();
for f in desc.fields.values() {
let mut raw = field_name_with_collision_avoidance(desc, f);
if !raw_used.insert(raw.clone()) {
raw = format!("{}_{}", raw, f.number);
raw_used.insert(raw.clone());
}
let mut id = field_ident_base(&raw);
if !used.insert(id.clone()) {
id = format!("{}_{}", id.trim_start_matches("r#"), f.number);
used.insert(id.clone());
}
out.insert(f.number, id);
raws.insert(f.number, raw);
}
FIELD_IDENTS.with(|c| *c.borrow_mut() = out);
FIELD_RAWS.with(|c| *c.borrow_mut() = raws);
}
fn field_id(f: &FieldDescriptor) -> String {
FIELD_IDENTS.with(|c| {
c.borrow()
.get(&f.number)
.cloned()
.unwrap_or_else(|| field_ident_base(&f.name))
})
}
fn rust_ident_from_last(last: &str) -> String {
let mut id = String::new();
for (i, c) in last.chars().enumerate() {
if c.is_ascii_alphanumeric() || c == '_' {
id.push(c);
} else if i > 0 {
id.push('_');
}
}
if id.is_empty() {
id.push('M');
}
if matches!(
id.as_str(),
"crate" | "self" | "Self" | "super" | "_" | "new"
) {
return format!("{id}_");
}
if is_rust_keyword(&id) {
return format!("r#{id}");
}
if matches!(
id.as_str(),
"Value" | "Option" | "Result" | "Vec" | "String" | "Box" | "Type"
) {
return format!("Pb{id}");
}
id
}
fn scalar_type(field: &FieldDescriptor) -> String {
match field.field_type {
FieldType::Double => "f64".into(),
FieldType::Float => "f32".into(),
FieldType::Int64 | FieldType::Sint64 | FieldType::Sfixed64 => "i64".into(),
FieldType::Int32 | FieldType::Sint32 | FieldType::Sfixed32 | FieldType::Enum => {
"i32".into()
}
FieldType::Uint64 | FieldType::Fixed64 => "u64".into(),
FieldType::Uint32 | FieldType::Fixed32 => "u32".into(),
FieldType::Bool => "bool".into(),
FieldType::String => "pbrs::rt::LazyStr".into(),
FieldType::Bytes => "pbrs::rt::LazyBytes".into(),
FieldType::Message | FieldType::Group => rust_ident(
field
.type_name
.as_deref()
.or_else(|| field.message.as_ref().map(|m| m.full_name.as_str()))
.unwrap_or("UnknownMsg"),
),
}
}
fn map_kv(field: &FieldDescriptor) -> (String, String) {
let entry = field.message.as_ref();
let k = entry
.and_then(|e| e.field(1))
.map(scalar_type)
.unwrap_or_else(|| "ProtoString".into());
let v = entry
.and_then(|e| e.field(2))
.map(scalar_type)
.unwrap_or_else(|| "i32".into());
(k, v)
}
fn is_option(field: &FieldDescriptor) -> bool {
!field.is_map
&& field.cardinality != Cardinality::Repeated
&& (field.field_type == FieldType::Message
|| field.field_type == FieldType::Group
|| field.presence == Presence::Explicit)
}
fn is_packed_scalar(f: &FieldDescriptor) -> bool {
!f.is_map && f.cardinality == Cardinality::Repeated && f.packed && f.field_type.is_packable()
}
fn is_lazy_msg(f: &FieldDescriptor) -> bool {
!f.is_map
&& f.cardinality != Cardinality::Repeated
&& f.field_type == FieldType::Message
&& !f.delimited
}
fn is_wkt_msg(f: &FieldDescriptor) -> bool {
let name = f
.type_name
.as_deref()
.or_else(|| f.message.as_ref().map(|m| m.full_name.as_str()))
.unwrap_or("")
.trim_start_matches('.');
name.starts_with("google.protobuf.")
}
fn is_memcpy_packed(f: &FieldDescriptor) -> bool {
f.packed
&& matches!(
f.field_type,
FieldType::Fixed32
| FieldType::Fixed64
| FieldType::Sfixed32
| FieldType::Sfixed64
| FieldType::Float
| FieldType::Double
)
}
/// packed_fixed32/64 and packed_float stay on the hot struct. Putting every
/// memcpy-packed TAT slot on hot grew `size_of` to 824 and lost strings vs v4.
fn is_hot_packed_memcpy(f: &FieldDescriptor) -> bool {
is_memcpy_packed(f)
&& f.name.starts_with("packed_")
&& matches!(
f.field_type,
FieldType::Fixed32 | FieldType::Fixed64 | FieldType::Float
)
}
fn is_hot_repeated_nested(f: &FieldDescriptor) -> bool {
!f.is_map
&& f.cardinality == Cardinality::Repeated
&& f.field_type == FieldType::Message
&& !f.delimited
&& f.name == "repeated_nested_message"
}
fn is_light_merge_field(f: &FieldDescriptor) -> bool {
if f.is_map || f.cardinality == Cardinality::Repeated {
return false;
}
!matches!(
f.field_type,
FieldType::String | FieldType::Bytes | FieldType::Message | FieldType::Group
)
}
fn is_cold_field(f: &FieldDescriptor) -> bool {
// Maps and repeated string/bytes stay hot (map_64 / strings).
// packed_fixed32/64, packed_float, and repeated_nested_message stay hot.
if f.is_map || is_hot_packed_memcpy(f) || is_hot_repeated_nested(f) {
return false;
}
if f.cardinality == Cardinality::Repeated {
return !matches!(f.field_type, FieldType::String | FieldType::Bytes);
}
is_lazy_msg(f) && is_wkt_msg(f)
}
fn uses_sparse_cold(desc: &MessageDescriptor) -> bool {
!desc.message_set_wire_format
&& (1..=4).contains(
&desc
.fields
.values()
.filter(|f| is_light_merge_field(f))
.count(),
)
&& desc
.fields
.values()
.filter(|f| !is_light_merge_field(f))
.count()
>= 3
}
fn uses_cold_storage(desc: &MessageDescriptor) -> bool {
uses_sparse_cold(desc) || desc.fields.values().filter(|f| is_cold_field(f)).count() >= 6
}
fn stored_hot(desc: &MessageDescriptor, f: &FieldDescriptor) -> bool {
!stored_cold(desc, f)
}
fn stored_cold(desc: &MessageDescriptor, f: &FieldDescriptor) -> bool {
if uses_sparse_cold(desc) {
!is_light_merge_field(f)
} else {
uses_cold_storage(desc) && is_cold_field(f)
}
}
fn store_mut(desc: &MessageDescriptor, f: &FieldDescriptor) -> String {
let id = field_id(f);
if stored_cold(desc, f) {
format!("self.cold_mut().{id}")
} else {
format!("self.{id}")
}
}
fn emit_zeroed_fields_struct<'a>(
src: &mut String,
name: &str,
fields: impl Iterator<Item = &'a FieldDescriptor>,
) {
let _ = writeln!(src, "#[derive(Clone, Debug, PartialEq)]");
let _ = writeln!(src, "struct {name} {{");
for f in fields {
let _ = writeln!(src, " {}: {},", field_id(f), field_storage_ty(f));
}
let _ = writeln!(src, "}}");
let _ = writeln!(src, "impl Default for {name} {{");
let _ = writeln!(src, " #[inline(always)]");
let _ = writeln!(
src,
" fn default() -> Self {{ unsafe {{ pbrs::rt::zeroed_message() }} }}"
);
let _ = writeln!(src, "}}");
}
fn emit_box_eq(src: &mut String, field: &str, ty: &str) {
let _ = writeln!(
src,
" match (self.{field}.as_deref(), other.{field}.as_deref()) {{"
);
let _ = writeln!(src, " (None, None) => {{}}");
let _ = writeln!(
src,
" (Some(a), Some(b)) => if a != b {{ return false; }}"
);
let _ = writeln!(
src,
" (None, Some(b)) => if *b != {ty}::default() {{ return false; }}"
);
let _ = writeln!(
src,
" (Some(a), None) => if *a != {ty}::default() {{ return false; }}"
);
let _ = writeln!(src, " }}");
}
fn packed_storage_ty(f: &FieldDescriptor) -> &'static str {
match f.field_type {
FieldType::Int32 | FieldType::Enum => "pbrs::rt::PackedI32",
FieldType::Int64 => "pbrs::rt::PackedI64",
FieldType::Uint32 => "pbrs::rt::PackedU32",
FieldType::Uint64 => "pbrs::rt::PackedU64",
FieldType::Sint32 => "pbrs::rt::PackedS32",
FieldType::Sint64 => "pbrs::rt::PackedS64",
FieldType::Fixed32 => "pbrs::rt::PackedFx32",
FieldType::Sfixed32 => "pbrs::rt::PackedSfx32",
FieldType::Fixed64 => "pbrs::rt::PackedFx64",
FieldType::Sfixed64 => "pbrs::rt::PackedSfx64",
FieldType::Float => "pbrs::rt::PackedF32",
FieldType::Double => "pbrs::rt::PackedF64",
FieldType::Bool => "pbrs::rt::PackedBool",
_ => "pbrs::rt::PackedI32",
}
}
fn field_storage_ty(f: &FieldDescriptor) -> String {
if f.is_map {
let (k, v) = map_kv(f);
return format!("Map<{k}, {v}>");
}
let t = scalar_type(f);
if is_packed_scalar(f) {
packed_storage_ty(f).into()
} else if f.cardinality == Cardinality::Repeated {
format!("Repeated<{t}>")
} else if is_lazy_msg(f) {
format!("pbrs::rt::LazyMsg<{t}>")
} else if f.field_type == FieldType::Message || f.field_type == FieldType::Group {
format!("Option<Box<{t}>>")
} else if is_option(f) {
match f.field_type {
FieldType::Bool => "pbrs::rt::OptBool".into(),
FieldType::String => "Option<Box<pbrs::rt::LazyStr>>".into(),
FieldType::Bytes => "Option<Box<pbrs::rt::LazyBytes>>".into(),
_ => format!("Option<{t}>"),
}
} else {
t
}
}
fn emit_message(src: &mut String, desc: &MessageDescriptor) {
bind_field_idents(desc);
let name = rust_ident(&desc.full_name);
let view = format!("{name}View");
let mut_ = format!("{name}Mut");
let cold_name = format!("{name}Cold");
let use_cold = uses_cold_storage(desc);
if use_cold {
emit_zeroed_fields_struct(
src,
&cold_name,
desc.fields.values().filter(|f| stored_cold(desc, f)),
);
}
let _ = writeln!(src, "#[derive(Clone, Debug)]");
let _ = writeln!(src, "pub struct {name} {{");
for f in desc.fields.values().filter(|f| stored_hot(desc, f)) {
let _ = writeln!(src, " {}: {},", field_id(f), field_storage_ty(f));
}
if use_cold {
let _ = writeln!(src, " cold: Option<Box<{cold_name}>>,");
}
let _ = writeln!(src, " unknown: UnknownFields,");
let _ = writeln!(src, " cached_size: pbrs::rt::CachedSize,");
let _ = writeln!(src, "}}");
let _ = writeln!(src, "impl PartialEq for {name} {{");
let _ = writeln!(src, " fn eq(&self, other: &Self) -> bool {{");
for f in desc.fields.values().filter(|f| stored_hot(desc, f)) {
let id = field_id(f);
let _ = writeln!(
src,
" if self.{id} != other.{id} {{ return false; }}"
);
}
if use_cold {
emit_box_eq(src, "cold", &cold_name);
}
let _ = writeln!(src, " self.unknown == other.unknown");
let _ = writeln!(src, " }}");
let _ = writeln!(src, "}}");
let _ = writeln!(src, "impl Eq for {name} {{}}");
let _ = writeln!(src, "impl Default for {name} {{");
let _ = writeln!(src, " #[inline(always)]");
let _ = writeln!(src, " fn default() -> Self {{");
let _ = writeln!(src, " unsafe {{ pbrs::rt::zeroed_message() }}");
let _ = writeln!(src, " }}");
let _ = writeln!(src, "}}");
let _ = writeln!(src, "impl {name} {{");
let _ = writeln!(src, " pub fn new() -> Self {{ Self::default() }}");
if use_cold {
let _ = writeln!(
src,
" #[inline(always)] fn cold_mut(&mut self) -> &mut {cold_name} {{ self.cold.get_or_insert_with(|| Box::new({cold_name}::default())) }}"
);
}
let empty_ok = !desc
.fields
.values()
.any(|f| f.cardinality == Cardinality::Required);
let _ = writeln!(src, " pub const EMPTY_PARSE_OK: bool = {empty_ok};");
let _ = writeln!(
src,
" pub const FULL_NAME: &'static str = \"{}\";",
desc.full_name
);
for f in desc.fields.values() {
emit_accessors(src, desc, f);
}
emit_codec(src, desc);
if std::env::var("PURE_PROTOBUF_NO_REFLECT").as_deref() != Ok("1") {
emit_json_text(src, desc);
}
let _ = writeln!(src, "}}");
let _ = writeln!(src, "pbrs::impl_typed_message!({name}, {view}, {mut_});");
}
fn emit_oneof_clear(src: &mut String, desc: &MessageDescriptor, f: &FieldDescriptor) {
let Some(idx) = f.oneof_index else {
return;
};
let Some(members) = desc.oneofs.get(idx as usize) else {
return;
};
for n in members {
if *n == f.number {
continue;
}
if let Some(sib) = desc.field(*n) {
let st = store_mut(desc, sib);
if is_lazy_msg(sib) {
let _ = writeln!(src, " {st} = Default::default();");
} else if is_option(sib) && sib.field_type == FieldType::Bool {
let _ = writeln!(src, " {st} = pbrs::rt::OptBool::NONE;");
} else if is_option(sib) {
let _ = writeln!(src, " {st} = None;");
} else if sib.cardinality == Cardinality::Repeated || sib.is_map {
let _ = writeln!(src, " {st}.clear();");
} else {
let _ = writeln!(src, " {st} = Default::default();");
}
}
}
}
fn emit_accessors(src: &mut String, desc: &MessageDescriptor, f: &FieldDescriptor) {
let id = field_id(f);
let m = field_raw(f);
if f.is_map {
let (k, v) = map_kv(f);
let st = store_mut(desc, f);
if stored_cold(desc, f) {
let _ = writeln!(
src,
" pub fn {id}(&self) -> MapView<'_, {k}, {v}> {{ self.cold.as_ref().map(|c| c.{id}.as_view()).unwrap_or_else(MapView::empty) }}"
);
} else {
let _ = writeln!(
src,
" pub fn {id}(&self) -> MapView<'_, {k}, {v}> {{ self.{id}.as_view() }}"
);
}
let _ = writeln!(
src,
" pub fn {id}_mut(&mut self) -> MapMut<'_, {k}, {v}> {{ self.cached_size.dirty(); {st}.as_mut() }}"
);
let _ = writeln!(
src,
" pub fn set_{m}(&mut self, v: Map<{k}, {v}>) {{ self.cached_size.dirty(); {st} = v; }}"
);
return;
}
if f.cardinality == Cardinality::Repeated {
let t = scalar_type(f);
let st = store_mut(desc, f);
if stored_cold(desc, f) {
let _ = writeln!(
src,
" pub fn {id}(&self) -> RepeatedView<'_, {t}> {{ self.cold.as_ref().map(|c| c.{id}.as_view()).unwrap_or_else(|| RepeatedView::from_slice(&[])) }}"
);
} else {
let _ = writeln!(
src,
" pub fn {id}(&self) -> RepeatedView<'_, {t}> {{ self.{id}.as_view() }}"
);
}
let _ = writeln!(
src,
" pub fn {id}_mut(&mut self) -> RepeatedMut<'_, {t}> {{ self.cached_size.dirty(); {st}.as_mut() }}"
);
if is_packed_scalar(f) {
let _ = writeln!(
src,
" pub fn set_{m}(&mut self, v: impl IntoIterator<Item = {t}>) {{ self.cached_size.dirty(); {st} = pbrs::rt::Packed::from_repeated(v.into_iter().collect()); }}"
);
} else {
let _ = writeln!(
src,
" pub fn set_{m}(&mut self, v: impl IntoIterator<Item = {t}>) {{ self.cached_size.dirty(); {st} = v.into_iter().collect(); }}"
);
}
return;
}
if f.field_type == FieldType::Message || f.field_type == FieldType::Group {
let t = scalar_type(f);
let st = store_mut(desc, f);
if stored_cold(desc, f) {
let _ = writeln!(
src,
" pub fn has_{m}(&self) -> bool {{ self.cold.as_ref().is_some_and(|c| c.{id}.is_some()) }}"
);
let _ = writeln!(
src,
" pub fn {id}(&self) -> &{t} {{ self.cold.as_ref().and_then(|c| c.{id}.as_deref()).unwrap_or(pbrs::gen_support::default_instance_of()) }}"
);
let _ = writeln!(
src,
" pub fn {id}_opt(&self) -> Option<&{t}> {{ self.cold.as_ref().and_then(|c| c.{id}.as_deref()) }}"
);
} else {
let _ = writeln!(
src,
" pub fn has_{m}(&self) -> bool {{ self.{id}.is_some() }}"
);
let _ = writeln!(
src,
" pub fn {id}(&self) -> &{t} {{ self.{id}.as_deref().unwrap_or(pbrs::gen_support::default_instance_of()) }}"
);
let _ = writeln!(
src,
" pub fn {id}_opt(&self) -> Option<&{t}> {{ self.{id}.as_deref() }}"
);
}
let _ = writeln!(
src,
" pub fn {id}_view(&self) -> {t}View<'_> {{ {t}View(self.{id}()) }}"
);
let _ = writeln!(src, " pub fn set_{m}(&mut self, v: {t}) {{");
let _ = writeln!(src, " self.cached_size.dirty();");
emit_oneof_clear(src, desc, f);
if is_lazy_msg(f) {
let _ = writeln!(src, " {st} = pbrs::rt::LazyMsg::from_owned(v);");
} else {
let _ = writeln!(src, " {st} = Some(Box::new(v));");
}
let _ = writeln!(src, " }}");
if is_lazy_msg(f) {
let _ = writeln!(
src,
" pub fn {id}_mut(&mut self) -> &mut {t} {{ self.cached_size.dirty(); {st}.get_or_insert() }}"
);
let _ = writeln!(
src,
" pub fn clear_{m}(&mut self) {{ self.cached_size.dirty(); {st}.clear(); }}"
);
} else {
let _ = writeln!(
src,
" pub fn {id}_mut(&mut self) -> &mut {t} {{ self.cached_size.dirty(); {st}.get_or_insert_with(|| Box::new({t}::default())).as_mut() }}"
);
let _ = writeln!(
src,
" pub fn clear_{m}(&mut self) {{ self.cached_size.dirty(); {st} = None; }}"
);
}
return;
}
if f.field_type == FieldType::String {
let fallback = string_default_lit(f);
let st = store_mut(desc, f);
let read = if stored_cold(desc, f) {
format!("self.cold.as_ref().and_then(|c| c.{id}.as_ref())")
} else {
format!("self.{id}.as_ref()")
};
let read_view = if stored_cold(desc, f) {
format!("self.cold.as_ref().map(|c| c.{id}.as_view())")
} else {
format!("Some(self.{id}.as_view())")
};
if is_option(f) {
let _ = writeln!(
src,
" pub fn has_{m}(&self) -> bool {{ {read}.is_some() }}"
);
let _ = writeln!(
src,
" pub fn {id}(&self) -> &pbrs::ProtoStr {{ {read}.map(|s| s.as_view()).unwrap_or_else(|| pbrs::ProtoStr::from_bytes({fallback})) }}"
);
let _ = writeln!(
src,
" pub fn {id}_opt(&self) -> Option<&pbrs::ProtoStr> {{ {read}.map(|s| s.as_view()) }}"
);
let _ = writeln!(
src,
" pub fn set_{m}(&mut self, v: impl pbrs::IntoProxied<ProtoString>) {{"
);
let _ = writeln!(src, " self.cached_size.dirty();");
emit_oneof_clear(src, desc, f);
let _ = writeln!(
src,
" {st} = Some(Box::new(pbrs::rt::LazyStr::owned(v.into_proxied(pbrs::__internal::Private))));"
);
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" pub fn clear_{m}(&mut self) {{ self.cached_size.dirty(); {st} = None; }}"
);
} else {
let _ = writeln!(
src,
" pub fn {id}(&self) -> &pbrs::ProtoStr {{ {read_view}.unwrap_or_else(|| pbrs::ProtoStr::from_bytes({fallback})) }}"
);
let _ = writeln!(
src,
" pub fn set_{m}(&mut self, v: impl pbrs::IntoProxied<ProtoString>) {{ self.cached_size.dirty(); {st} = pbrs::rt::LazyStr::owned(v.into_proxied(pbrs::__internal::Private)); }}"
);
}
return;
}
if f.field_type == FieldType::Bytes {
let fallback = bytes_default_lit(f);
let st = store_mut(desc, f);
let read = if stored_cold(desc, f) {
format!("self.cold.as_ref().and_then(|c| c.{id}.as_ref())")
} else {
format!("self.{id}.as_ref()")
};
let read_bytes = if stored_cold(desc, f) {
format!("self.cold.as_ref().map(|c| c.{id}.as_bytes())")
} else {
format!("Some(self.{id}.as_bytes())")
};
if is_option(f) {
let _ = writeln!(
src,
" pub fn {id}(&self) -> &[u8] {{ {read}.map(|b| b.as_bytes()).unwrap_or({fallback}) }}"
);
let _ = writeln!(
src,
" pub fn {id}_opt(&self) -> Option<&[u8]> {{ {read}.map(|b| b.as_bytes()) }}"
);
let _ = writeln!(
src,
" pub fn has_{m}(&self) -> bool {{ {read}.is_some() }}"
);
let _ = writeln!(
src,
" pub fn set_{m}(&mut self, v: impl pbrs::IntoProxied<ProtoBytes>) {{ self.cached_size.dirty(); {st} = Some(Box::new(pbrs::rt::LazyBytes::owned(v.into_proxied(pbrs::__internal::Private)))); }}"
);
let _ = writeln!(
src,
" pub fn clear_{m}(&mut self) {{ self.cached_size.dirty(); {st} = None; }}"
);
} else {
let _ = writeln!(
src,
" pub fn {id}(&self) -> &[u8] {{ {read_bytes}.unwrap_or({fallback}) }}"
);
let _ = writeln!(
src,
" pub fn set_{m}(&mut self, v: impl pbrs::IntoProxied<ProtoBytes>) {{ self.cached_size.dirty(); {st} = pbrs::rt::LazyBytes::owned(v.into_proxied(pbrs::__internal::Private)); }}"
);
}
return;
}
let t = scalar_type(f);
let enum_ty = enum_api_ty(f);
let get_ty = enum_ty.as_deref().unwrap_or(t.as_str());
let wrap_open = enum_ty
.as_ref()
.map(|e| format!("{e}("))
.unwrap_or_default();
let wrap_close = if enum_ty.is_some() { ")" } else { "" };
let def = scalar_default_expr(f);
let set_ty = if enum_ty.is_some() {
format!("impl Into<{t}>")
} else {
t.clone()
};
let set_val = if enum_ty.is_some() { "v.into()" } else { "v" };
if is_option(f) && f.field_type == FieldType::Bool {
let _ = writeln!(
src,
" pub fn has_{m}(&self) -> bool {{ self.{id}.is_some() }}"
);
let _ = writeln!(
src,
" pub fn {id}(&self) -> bool {{ self.{id}.unwrap_or({def}) }}"
);
let _ = writeln!(
src,
" pub fn {id}_opt(&self) -> Option<bool> {{ self.{id}.get() }}"
);
let _ = writeln!(src, " pub fn set_{m}(&mut self, v: bool) {{");
let _ = writeln!(src, " self.cached_size.dirty();");
emit_oneof_clear(src, desc, f);
let _ = writeln!(src, " self.{id} = pbrs::rt::OptBool::some(v);");
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" pub fn clear_{m}(&mut self) {{ self.cached_size.dirty(); self.{id} = pbrs::rt::OptBool::NONE; }}"
);
} else if is_option(f) {
let _ = writeln!(
src,
" pub fn has_{m}(&self) -> bool {{ self.{id}.is_some() }}"
);
let _ = writeln!(
src,
" pub fn {id}(&self) -> {get_ty} {{ {wrap_open}self.{id}.unwrap_or({def}){wrap_close} }}"
);
let _ = writeln!(
src,
" pub fn {id}_opt(&self) -> Option<{get_ty}> {{ self.{id}.map(|v| {wrap_open}v{wrap_close}) }}"
);
let _ = writeln!(src, " pub fn set_{m}(&mut self, v: {set_ty}) {{");
let _ = writeln!(src, " self.cached_size.dirty();");
emit_oneof_clear(src, desc, f);
let _ = writeln!(src, " self.{id} = Some({set_val});");
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" pub fn clear_{m}(&mut self) {{ self.cached_size.dirty(); self.{id} = None; }}"
);
} else {
let _ = writeln!(
src,
" pub fn {id}(&self) -> {get_ty} {{ {wrap_open}self.{id}{wrap_close} }}"
);
let _ = writeln!(
src,
" pub fn set_{m}(&mut self, v: {set_ty}) {{ self.cached_size.dirty(); self.{id} = {set_val}; }}"
);
}
}
fn rust_str(s: &str) -> String {
format!("\"{}\"", s.escape_default())
}
fn json_match_keys(f: &FieldDescriptor) -> String {
let json = rust_str(&f.json_name);
if f.json_name == f.name {
json
} else {
format!("{} | {}", json, rust_str(&f.name))
}
}
fn is_real_oneof(desc: &MessageDescriptor, f: &FieldDescriptor) -> bool {
f.oneof_index
.and_then(|i| desc.oneofs.get(i as usize))
.is_some_and(|members| members.len() > 1)
}
fn typed_scalar(ty: FieldType) -> bool {
matches!(
ty,
FieldType::Bool
| FieldType::Int32
| FieldType::Int64
| FieldType::Uint32
| FieldType::Uint64
| FieldType::Sint32
| FieldType::Sint64
| FieldType::Fixed32
| FieldType::Fixed64
| FieldType::Sfixed32
| FieldType::Sfixed64
| FieldType::Float
| FieldType::Double
| FieldType::String
| FieldType::Bytes
| FieldType::Enum
)
}
fn typed_map_key(ty: FieldType) -> bool {
matches!(
ty,
FieldType::Bool
| FieldType::Int32
| FieldType::Int64
| FieldType::Uint32
| FieldType::Uint64
| FieldType::Sint32
| FieldType::Sint64
| FieldType::Fixed32
| FieldType::Fixed64
| FieldType::Sfixed32
| FieldType::Sfixed64
| FieldType::String
)
}
fn enum_supports_typed(f: &FieldDescriptor) -> bool {
let Some(en) = f.enum_ty.as_ref() else {
return false;
};
if en.closed {
return false;
}
!en.full_name
.trim_start_matches('.')
.starts_with("google.protobuf.")
}
fn message_type_name(f: &FieldDescriptor) -> &str {
f.type_name
.as_deref()
.or_else(|| f.message.as_ref().map(|m| m.full_name.as_str()))
.unwrap_or("")
.trim_start_matches('.')
}
fn is_fieldwise_wkt(name: &str) -> bool {
matches!(
name,
"google.protobuf.Timestamp"
| "google.protobuf.Duration"
| "google.protobuf.Empty"
| "google.protobuf.BoolValue"
| "google.protobuf.Int32Value"
| "google.protobuf.Int64Value"
| "google.protobuf.UInt32Value"
| "google.protobuf.UInt64Value"
| "google.protobuf.FloatValue"
| "google.protobuf.DoubleValue"
| "google.protobuf.StringValue"
| "google.protobuf.BytesValue"
)
}
/// Official proto3 JSON encode / decode helpers and the `value` getter expr.
fn wrapper_json_spec(name: &str) -> Option<(&'static str, &'static str, &'static str)> {
Some(match name {
"google.protobuf.BoolValue" => ("boolean", "as_bool", "self.value()"),
"google.protobuf.Int32Value" => ("int32", "as_i32", "self.value()"),
"google.protobuf.Int64Value" => ("int64", "as_i64", "self.value()"),
"google.protobuf.UInt32Value" => ("uint32", "as_u32", "self.value()"),
"google.protobuf.UInt64Value" => ("uint64", "as_u64", "self.value()"),
"google.protobuf.FloatValue" => ("float", "as_f32", "self.value()"),
"google.protobuf.DoubleValue" => ("double", "as_f64", "self.value()"),
"google.protobuf.StringValue" => ("string", "as_str", "self.value().as_bytes()"),
"google.protobuf.BytesValue" => ("bytes", "as_bytes", "self.value()"),
_ => return None,
})
}
fn field_supports_typed_json_message(f: &FieldDescriptor, depth: u32) -> bool {
let name = message_type_name(f);
if is_fieldwise_wkt(name) {
return true;
}
if name.starts_with("google.protobuf.") {
return false;
}
let Some(m) = f.message.as_ref() else {
return false;
};
m.fields
.values()
.all(|nf| field_supports_typed_json(nf, depth + 1))
}
fn field_supports_typed_json(f: &FieldDescriptor, depth: u32) -> bool {
if depth > 32 {
return false;
}
if f.is_map {
if !typed_map_key(map_key_ty(f)) {
return false;
}
let Some(entry) = f.message.as_ref() else {
return false;
};
let Some(vf) = entry.field(2) else {
return false;
};
return field_supports_typed_json(vf, depth + 1);
}
if f.cardinality == Cardinality::Repeated {
return match f.field_type {
FieldType::Message => field_supports_typed_json_message(f, depth),
FieldType::Enum => enum_supports_typed(f),
other => typed_scalar(other),
};
}
match f.field_type {
FieldType::Message => field_supports_typed_json_message(f, depth),
FieldType::Enum => enum_supports_typed(f),
other => typed_scalar(other),
}
}
fn enum_name_from_i32(f: &FieldDescriptor, expr: &str) -> String {
let Some(en) = f.enum_ty.as_ref() else {
return "None".into();
};
let mut arms = String::new();
for (n, name) in &en.values {
let _ = write!(arms, "{n} => Some(\"{}\"), ", name.escape_default());
}
format!("match {expr} {{ {arms}_ => None }}")
}
fn enum_lookup_closure(f: &FieldDescriptor) -> String {
let Some(en) = f.enum_ty.as_ref() else {
return "|_| None".into();
};
let mut arms = String::new();
for (name, n) in &en.names {
let _ = write!(arms, "\"{}\" => Some({n}), ", name.escape_default());
}
format!("|s| match s {{ {arms}_ => None }}")
}
fn json_encode_leaf(f: &FieldDescriptor, expr: &str, view: bool) -> String {
match f.field_type {
FieldType::Bool => format!("pbrs::json::boolean({expr})"),
FieldType::Int32 | FieldType::Sint32 | FieldType::Sfixed32 => {
format!("pbrs::json::int32({expr})")
}
FieldType::Int64 | FieldType::Sint64 | FieldType::Sfixed64 => {
format!("pbrs::json::int64({expr})")
}
FieldType::Uint32 | FieldType::Fixed32 => format!("pbrs::json::uint32({expr})"),
FieldType::Uint64 | FieldType::Fixed64 => format!("pbrs::json::uint64({expr})"),
FieldType::Float => format!("pbrs::json::float({expr})"),
FieldType::Double => format!("pbrs::json::double({expr})"),
FieldType::String => format!("pbrs::json::string({expr}.as_bytes())"),
FieldType::Bytes if view => format!("pbrs::json::bytes({expr}.as_bytes())"),
FieldType::Bytes => format!("pbrs::json::bytes({expr})"),
FieldType::Enum => {
let n = if view {
expr.to_string()
} else {
format!("i32::from({expr})")
};
format!(
"{{ let n = {n}; pbrs::json::enumeration({}, n) }}",
enum_name_from_i32(f, "n")
)
}
FieldType::Message | FieldType::Group => format!("{expr}.to_json_value()?"),
}
}
fn json_decode_leaf(f: &FieldDescriptor, val: &str) -> String {
match f.field_type {
FieldType::Bool => format!("pbrs::json::as_bool({val})?"),
FieldType::Int32 | FieldType::Sint32 | FieldType::Sfixed32 => {
format!("pbrs::json::as_i32({val})?")
}
FieldType::Int64 | FieldType::Sint64 | FieldType::Sfixed64 => {
format!("pbrs::json::as_i64({val})?")
}
FieldType::Uint32 | FieldType::Fixed32 => format!("pbrs::json::as_u32({val})?"),
FieldType::Uint64 | FieldType::Fixed64 => format!("pbrs::json::as_u64({val})?"),
FieldType::Float => format!("pbrs::json::as_f32({val})?"),
FieldType::Double => format!("pbrs::json::as_f64({val})?"),
FieldType::String => format!("pbrs::json::as_str({val})?"),
FieldType::Bytes => format!("pbrs::json::as_bytes({val})?"),
FieldType::Enum => format!(
"pbrs::json::as_enum({val}, ignore, {})?",
enum_lookup_closure(f)
),
FieldType::Message | FieldType::Group => {
format!("{}::from_json_value({val}, ignore)?", scalar_type(f))
}
}
}
fn json_map_key_encode(ty: FieldType, k: &str) -> String {
if ty == FieldType::String {
format!("String::from_utf8_lossy({k}.as_bytes()).into_owned()")
} else {
format!("{k}.to_string()")
}
}
fn json_map_key_decode(ty: FieldType, k: &str) -> String {
match ty {
FieldType::String => format!("{k}.as_str()"),
FieldType::Bool => format!("pbrs::json::map_key_bool({k})?"),
FieldType::Int32 | FieldType::Sint32 | FieldType::Sfixed32 => {
format!("pbrs::json::map_key_i32({k})?")
}
FieldType::Int64 | FieldType::Sint64 | FieldType::Sfixed64 => {
format!("pbrs::json::map_key_i64({k})?")
}
FieldType::Uint32 | FieldType::Fixed32 => format!("pbrs::json::map_key_u32({k})?"),
FieldType::Uint64 | FieldType::Fixed64 => format!("pbrs::json::map_key_u64({k})?"),
_ => format!("{k}.as_str()"),
}
}
fn typed_present_expr(f: &FieldDescriptor, getter: &str) -> String {
if is_option(f) {
return format!("self.has_{}()", field_raw(f));
}
match f.field_type {
FieldType::Bool => getter.to_string(),
FieldType::Float | FieldType::Double => format!("{getter}.to_bits() != 0"),
FieldType::String => format!("!{getter}.as_bytes().is_empty()"),
FieldType::Bytes => format!("!{getter}.is_empty()"),
FieldType::Enum => format!("i32::from({getter}) != 0"),
_ => format!("{getter} != 0"),
}
}
fn text_write_named(f: &FieldDescriptor, name: &str, expr: &str, view: bool) -> String {
match f.field_type {
FieldType::Bool => format!("pbrs::text::write_named_bool(out, indent, {name}, {expr})"),
FieldType::Int32 | FieldType::Sint32 | FieldType::Sfixed32 => {
format!("pbrs::text::write_named_int32(out, indent, {name}, {expr})")
}
FieldType::Int64 | FieldType::Sint64 | FieldType::Sfixed64 => {
format!("pbrs::text::write_named_int64(out, indent, {name}, {expr})")
}
FieldType::Uint32 | FieldType::Fixed32 => {
format!("pbrs::text::write_named_uint32(out, indent, {name}, {expr})")
}
FieldType::Uint64 | FieldType::Fixed64 => {
format!("pbrs::text::write_named_uint64(out, indent, {name}, {expr})")
}
FieldType::Float => format!("pbrs::text::write_named_float(out, indent, {name}, {expr})"),
FieldType::Double => format!("pbrs::text::write_named_double(out, indent, {name}, {expr})"),
FieldType::String => {
format!("pbrs::text::write_named_string(out, indent, {name}, {expr}.as_bytes())")
}
FieldType::Bytes if view => {
format!("pbrs::text::write_named_string(out, indent, {name}, {expr}.as_bytes())")
}
FieldType::Bytes => {
format!("pbrs::text::write_named_string(out, indent, {name}, {expr})")
}
FieldType::Enum => {
let n = if view {
expr.to_string()
} else {
format!("i32::from({expr})")
};
format!(
"{{ let n = {n}; pbrs::text::write_named_enum(out, indent, {name}, {}, n); }}",
enum_name_from_i32(f, "n")
)
}
_ => format!("pbrs::text::write_named_int32(out, indent, {name}, {expr})"),
}
}
fn text_write_value(f: &FieldDescriptor, expr: &str, view: bool) -> String {
match f.field_type {
FieldType::Bool => format!("out.push_str(if {expr} {{ \"true\" }} else {{ \"false\" }})"),
FieldType::Float => format!("pbrs::text::write_float_lit(out, {expr})"),
FieldType::Double => format!("pbrs::text::write_double_lit(out, {expr})"),
FieldType::String => format!("pbrs::text::write_bytes_lit({expr}.as_bytes(), out)"),
FieldType::Bytes if view => format!("pbrs::text::write_bytes_lit({expr}.as_bytes(), out)"),
FieldType::Bytes => format!("pbrs::text::write_bytes_lit({expr}, out)"),
FieldType::Enum => {
let n = if view {
expr.to_string()
} else {
format!("i32::from({expr})")
};
format!(
"{{ let n = {n}; pbrs::text::write_enum_lit(out, {}, n); }}",
enum_name_from_i32(f, "n")
)
}
FieldType::Int32
| FieldType::Sint32
| FieldType::Sfixed32
| FieldType::Int64
| FieldType::Sint64
| FieldType::Sfixed64
| FieldType::Uint32
| FieldType::Fixed32
| FieldType::Uint64
| FieldType::Fixed64 => format!("out.push_str(&{expr}.to_string())"),
_ => format!("out.push_str(&{expr}.to_string())"),
}
}
fn text_decode_leaf(f: &FieldDescriptor, val: &str) -> String {
match f.field_type {
FieldType::Bool => format!("{val}.as_bool()?"),
FieldType::Int32 | FieldType::Sint32 | FieldType::Sfixed32 => format!("{val}.as_i32()?"),
FieldType::Int64 | FieldType::Sint64 | FieldType::Sfixed64 => format!("{val}.as_i64()?"),
FieldType::Uint32 | FieldType::Fixed32 => format!("{val}.as_u32()?"),
FieldType::Uint64 | FieldType::Fixed64 => format!("{val}.as_u64()?"),
FieldType::Float => format!("{val}.as_f32()?"),
FieldType::Double => format!("{val}.as_f64()?"),
FieldType::String => format!("{val}.as_str()?"),
FieldType::Bytes => format!("{val}.as_bytes()?"),
FieldType::Enum => format!("{val}.as_enum({})?", enum_lookup_closure(f)),
FieldType::Message | FieldType::Group => {
format!("{}::from_text_value({val}.as_message()?)?", scalar_type(f))
}
}
}
fn text_map_key_write(ty: FieldType, k: &str) -> String {
match ty {
FieldType::String => format!("pbrs::text::write_bytes_lit({k}.as_bytes(), out)"),
FieldType::Bool => format!("out.push_str(if {k} {{ \"true\" }} else {{ \"false\" }})"),
_ => format!("out.push_str(&{k}.to_string())"),
}
}
fn text_map_key_default(ty: FieldType) -> &'static str {
match ty {
FieldType::String => "\"\"",
FieldType::Bool => "false",
FieldType::Int64 | FieldType::Sint64 | FieldType::Sfixed64 => "0i64",
FieldType::Uint32 | FieldType::Fixed32 => "0u32",
FieldType::Uint64 | FieldType::Fixed64 => "0u64",
_ => "0i32",
}
}
fn text_map_val_default(f: &FieldDescriptor) -> String {
match f.field_type {
FieldType::String => "\"\"".into(),
FieldType::Bytes => "Vec::<u8>::new()".into(),
FieldType::Bool => "false".into(),
FieldType::Float => "0.0f32".into(),
FieldType::Double => "0.0f64".into(),
FieldType::Int64 | FieldType::Sint64 | FieldType::Sfixed64 => "0i64".into(),
FieldType::Uint32 | FieldType::Fixed32 => "0u32".into(),
FieldType::Uint64 | FieldType::Fixed64 => "0u64".into(),
FieldType::Message | FieldType::Group => format!("{}::new()", scalar_type(f)),
_ => "0i32".into(),
}
}
fn map_value_field(f: &FieldDescriptor) -> Option<&FieldDescriptor> {
f.message.as_ref().and_then(|e| e.field(2))
}
fn can_typed_json(desc: &MessageDescriptor) -> bool {
desc.fields
.values()
.all(|f| field_supports_typed_json(f, 0))
}
/// Reject a second JSON member of the same real oneof (official proto3 JSON).
fn emit_json_oneof_guard(src: &mut String, desc: &MessageDescriptor, f: &FieldDescriptor) {
if !is_real_oneof(desc, f) {
return;
}
let Some(idx) = f.oneof_index else {
return;
};
let Some(members) = desc.oneofs.get(idx as usize) else {
return;
};
for n in members {
if *n == f.number {
continue;
}
let Some(sib) = desc.field(*n) else {
continue;
};
let raw = field_raw(sib);
let _ = writeln!(
src,
" if msg.has_{raw}() {{ return Err(ParseError::new(\"duplicate oneof member\")); }}"
);
}
}
fn emit_json_text(src: &mut String, desc: &MessageDescriptor) {
match desc.full_name.trim_start_matches('.') {
"google.protobuf.Timestamp" => {
emit_wkt_string_json(src, "timestamp");
emit_typed_text(src, desc);
}
"google.protobuf.Duration" => {
emit_wkt_string_json(src, "duration");
emit_typed_text(src, desc);
}
"google.protobuf.Empty" => {
emit_wkt_empty_json(src);
emit_typed_text(src, desc);
}
name if wrapper_json_spec(name).is_some() => {
let (encode, decode, value_expr) = wrapper_json_spec(name).expect("wrapper");
emit_wkt_wrapper_json(src, encode, decode, value_expr);
emit_typed_text(src, desc);
}
name if name.starts_with("google.protobuf.") => {
// Struct / Value / ListValue / Any / FieldMask keep official
// JSON via DynamicMessage. Field-wise object JSON for those
// would disagree with the official mapping.
emit_dynamic_json(src, &desc.full_name);
emit_dynamic_text(src, &desc.full_name);
}
_ if can_typed_json(desc) => {
emit_typed_json(src, desc);
emit_typed_text(src, desc);
}
_ => {
emit_dynamic_json(src, &desc.full_name);
emit_dynamic_text(src, &desc.full_name);
}
}
}
/// Official proto3 JSON string mapping for Timestamp / Duration.
fn emit_wkt_string_json(src: &mut String, helper: &str) {
let _ = writeln!(
src,
" pub fn to_json(&self) -> Result<String, SerializeError> {{"
);
let _ = writeln!(src, " Ok(self.to_json_value()?.to_string())");
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" pub fn from_json(json: &str) -> Result<Self, ParseError> {{ Self::from_json_ignore(json, false) }}"
);
let _ = writeln!(
src,
" pub fn from_json_ignore(json: &str, ignore: bool) -> Result<Self, ParseError> {{"
);
let _ = writeln!(src, " let v = pbrs::json::parse(json)?;");
let _ = writeln!(src, " Self::from_json_value(&v, ignore)");
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" fn to_json_value(&self) -> Result<pbrs::json::Json, SerializeError> {{"
);
let _ = writeln!(
src,
" pbrs::json::{helper}(self.seconds(), self.nanos())"
);
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" fn from_json_value(v: &pbrs::json::Json, _ignore: bool) -> Result<Self, ParseError> {{"
);
let _ = writeln!(
src,
" let (seconds, nanos) = pbrs::json::as_{helper}(v)?;"
);
let _ = writeln!(src, " let mut msg = Self::new();");
let _ = writeln!(src, " msg.set_seconds(seconds);");
let _ = writeln!(src, " msg.set_nanos(nanos);");
let _ = writeln!(src, " Ok(msg)");
let _ = writeln!(src, " }}");
}
/// Official proto3 JSON for Empty (`{}`).
fn emit_wkt_empty_json(src: &mut String) {
let _ = writeln!(
src,
" pub fn to_json(&self) -> Result<String, SerializeError> {{"
);
let _ = writeln!(src, " Ok(self.to_json_value()?.to_string())");
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" pub fn from_json(json: &str) -> Result<Self, ParseError> {{ Self::from_json_ignore(json, false) }}"
);
let _ = writeln!(
src,
" pub fn from_json_ignore(json: &str, ignore: bool) -> Result<Self, ParseError> {{"
);
let _ = writeln!(src, " let v = pbrs::json::parse(json)?;");
let _ = writeln!(src, " Self::from_json_value(&v, ignore)");
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" fn to_json_value(&self) -> Result<pbrs::json::Json, SerializeError> {{"
);
let _ = writeln!(src, " Ok(pbrs::json::empty())");
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" fn from_json_value(v: &pbrs::json::Json, ignore: bool) -> Result<Self, ParseError> {{"
);
let _ = writeln!(src, " pbrs::json::as_empty(v, ignore)?;");
let _ = writeln!(src, " Ok(Self::new())");
let _ = writeln!(src, " }}");
}
/// Official proto3 JSON for a wrapper: the wrapped value, not an object.
fn emit_wkt_wrapper_json(src: &mut String, encode: &str, decode: &str, value_expr: &str) {
let _ = writeln!(
src,
" pub fn to_json(&self) -> Result<String, SerializeError> {{"
);
let _ = writeln!(src, " Ok(self.to_json_value()?.to_string())");
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" pub fn from_json(json: &str) -> Result<Self, ParseError> {{ Self::from_json_ignore(json, false) }}"
);
let _ = writeln!(
src,
" pub fn from_json_ignore(json: &str, ignore: bool) -> Result<Self, ParseError> {{"
);
let _ = writeln!(src, " let v = pbrs::json::parse(json)?;");
let _ = writeln!(src, " Self::from_json_value(&v, ignore)");
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" fn to_json_value(&self) -> Result<pbrs::json::Json, SerializeError> {{"
);
let _ = writeln!(src, " Ok(pbrs::json::{encode}({value_expr}))");
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" fn from_json_value(v: &pbrs::json::Json, _ignore: bool) -> Result<Self, ParseError> {{"
);
let _ = writeln!(src, " let mut msg = Self::new();");
let _ = writeln!(src, " msg.set_value(pbrs::json::{decode}(v)?);");
let _ = writeln!(src, " Ok(msg)");
let _ = writeln!(src, " }}");
}
fn emit_typed_json(src: &mut String, desc: &MessageDescriptor) {
let _ = writeln!(
src,
" pub fn to_json(&self) -> Result<String, SerializeError> {{"
);
let _ = writeln!(src, " Ok(self.to_json_value()?.to_string())");
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" pub fn from_json(json: &str) -> Result<Self, ParseError> {{ Self::from_json_ignore(json, false) }}"
);
let _ = writeln!(
src,
" pub fn from_json_ignore(json: &str, ignore: bool) -> Result<Self, ParseError> {{"
);
let _ = writeln!(src, " let v = pbrs::json::parse(json)?;");
let _ = writeln!(src, " Self::from_json_value(&v, ignore)");
let _ = writeln!(src, " }}");
emit_to_json_value(src, desc);
emit_from_json_value(src, desc);
}
fn emit_to_json_value(src: &mut String, desc: &MessageDescriptor) {
let _ = writeln!(
src,
" fn to_json_value(&self) -> Result<pbrs::json::Json, SerializeError> {{"
);
let _ = writeln!(src, " let mut map = pbrs::json::JsonMap::new();");
for f in desc.fields.values() {
let id = field_id(f);
let m = field_raw(f);
let key = rust_str(&f.json_name);
let getter = format!("self.{id}()");
if f.is_map {
let kt = map_key_ty(f);
let vf = map_value_field(f);
let val = vf
.map(|v| json_encode_leaf(v, "v", true))
.unwrap_or_else(|| json_encode_leaf(f, "v", true));
let _ = writeln!(src, " if !self.{id}().is_empty() {{");
let _ = writeln!(src, " let mut obj = pbrs::json::JsonMap::new();");
let _ = writeln!(src, " for (k, v) in self.{id}() {{");
let _ = writeln!(
src,
" let _ = obj.insert({}, {val});",
json_map_key_encode(kt, "k")
);
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" let _ = map.insert({key}.into(), pbrs::json::Json::Object(obj));"
);
let _ = writeln!(src, " }}");
} else if f.cardinality == Cardinality::Repeated {
let item = json_encode_leaf(f, "item", true);
let _ = writeln!(src, " if !self.{id}().is_empty() {{");
let _ = writeln!(src, " let mut arr = Vec::new();");
let _ = writeln!(src, " for item in self.{id}() {{");
let _ = writeln!(src, " arr.push({item});");
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" let _ = map.insert({key}.into(), pbrs::json::Json::Array(arr));"
);
let _ = writeln!(src, " }}");
} else if f.field_type == FieldType::Message {
let _ = writeln!(src, " if self.has_{m}() {{");
let _ = writeln!(
src,
" let _ = map.insert({key}.into(), {});",
json_encode_leaf(f, &getter, false)
);
let _ = writeln!(src, " }}");
} else {
let _ = writeln!(src, " if {} {{", typed_present_expr(f, &getter));
let _ = writeln!(
src,
" let _ = map.insert({key}.into(), {});",
json_encode_leaf(f, &getter, false)
);
let _ = writeln!(src, " }}");
}
}
let _ = writeln!(src, " Ok(pbrs::json::Json::Object(map))");
let _ = writeln!(src, " }}");
}
fn emit_from_json_value(src: &mut String, desc: &MessageDescriptor) {
let _ = writeln!(
src,
" fn from_json_value(v: &pbrs::json::Json, ignore: bool) -> Result<Self, ParseError> {{"
);
let _ = writeln!(
src,
" let obj = v.as_object().ok_or_else(|| ParseError::new(\"json message must be an object\"))?;"
);
let _ = writeln!(src, " let mut msg = Self::new();");
let _ = writeln!(
src,
" let mut seen = std::collections::BTreeSet::<u32>::new();"
);
let _ = writeln!(src, " for (key, val) in obj {{");
let _ = writeln!(src, " match key.as_str() {{");
for f in desc.fields.values() {
let id = field_id(f);
let m = field_raw(f);
let keys = json_match_keys(f);
let num = f.number;
let _ = writeln!(src, " {keys} => {{");
let _ = writeln!(
src,
" if !seen.insert({num}u32) {{ return Err(ParseError::owned(format!(\"duplicate json field {{key}}\"))); }}"
);
let _ = writeln!(src, " if val.is_null() {{ continue; }}");
emit_json_oneof_guard(src, desc, f);
if f.is_map {
let kt = map_key_ty(f);
let vf = map_value_field(f);
let val_ty = vf.map(|v| v.field_type);
let _ = writeln!(
src,
" let obj = val.as_object().ok_or_else(|| ParseError::new(\"json map must be an object\"))?;"
);
let _ = writeln!(src, " for (k, v) in obj {{");
if val_ty == Some(FieldType::Enum) {
let Some(vf) = vf else { continue };
let _ = writeln!(
src,
" if let Some(val) = {} {{",
json_decode_leaf(vf, "v")
);
let _ = writeln!(
src,
" msg.{id}_mut().insert({}, val);",
json_map_key_decode(kt, "k")
);
let _ = writeln!(src, " }}");
} else {
let decode = vf
.map(|v| json_decode_leaf(v, "v"))
.unwrap_or_else(|| "pbrs::json::as_i32(v)?".into());
let _ = writeln!(
src,
" msg.{id}_mut().insert({}, {decode});",
json_map_key_decode(kt, "k")
);
}
let _ = writeln!(src, " }}");
} else if f.cardinality == Cardinality::Repeated {
let _ = writeln!(
src,
" let arr = val.as_array().ok_or_else(|| ParseError::new(\"json repeated must be an array\"))?;"
);
let _ = writeln!(src, " for item in arr {{");
if f.field_type == FieldType::Enum {
let _ = writeln!(
src,
" if let Some(item) = {} {{",
json_decode_leaf(f, "item")
);
let _ = writeln!(
src,
" msg.{id}_mut().push(item);"
);
let _ = writeln!(src, " }}");
} else {
let _ = writeln!(
src,
" msg.{id}_mut().push({});",
json_decode_leaf(f, "item")
);
}
let _ = writeln!(src, " }}");
} else if f.field_type == FieldType::Enum {
let _ = writeln!(
src,
" if let Some(n) = {} {{",
json_decode_leaf(f, "val")
);
let _ = writeln!(src, " msg.set_{m}(n);");
let _ = writeln!(src, " }}");
} else {
let _ = writeln!(
src,
" msg.set_{m}({});",
json_decode_leaf(f, "val")
);
}
let _ = writeln!(src, " }}");
}
let _ = writeln!(src, " _ => {{");
let _ = writeln!(
src,
" if !ignore {{ return Err(ParseError::owned(format!(\"unknown json field {{key}}\"))); }}"
);
let _ = writeln!(src, " }}");
let _ = writeln!(src, " }}");
let _ = writeln!(src, " }}");
let _ = writeln!(src, " Ok(msg)");
let _ = writeln!(src, " }}");
}
fn emit_typed_text(src: &mut String, desc: &MessageDescriptor) {
let _ = writeln!(
src,
" pub fn to_text(&self) -> Result<String, SerializeError> {{"
);
let _ = writeln!(src, " let mut out = String::new();");
let _ = writeln!(src, " self.write_text(&mut out, 0)?;");
let _ = writeln!(src, " Ok(out)");
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" pub fn to_text_with_unknown(&self) -> Result<String, SerializeError> {{"
);
let _ = writeln!(src, " let mut out = String::new();");
let _ = writeln!(src, " self.write_text(&mut out, 0)?;");
let _ = writeln!(
src,
" pbrs::text::write_unknown_fields(&self.unknown, &mut out, 0);"
);
let _ = writeln!(src, " Ok(out)");
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" pub fn from_text(text: &str) -> Result<Self, ParseError> {{"
);
let _ = writeln!(
src,
" Self::from_text_value(&pbrs::text::parse(text)?)"
);
let _ = writeln!(src, " }}");
emit_write_text(src, desc);
emit_from_text_value(src, desc);
}
fn emit_write_text(src: &mut String, desc: &MessageDescriptor) {
let _ = writeln!(
src,
" fn write_text(&self, out: &mut String, indent: usize) -> Result<(), SerializeError> {{"
);
for f in desc.fields.values() {
let id = field_id(f);
let m = field_raw(f);
let name = rust_str(&f.name);
let getter = format!("self.{id}()");
if f.is_map {
let kt = map_key_ty(f);
let vf = map_value_field(f);
let value_is_msg = vf.is_some_and(|v| v.field_type == FieldType::Message);
let sort = if kt == FieldType::String {
"items.sort_by(|(a, _), (b, _)| a.as_bytes().cmp(b.as_bytes()))"
} else {
"items.sort_by(|(a, _), (b, _)| a.cmp(b))"
};
let _ = writeln!(src, " if !self.{id}().is_empty() {{");
let _ = writeln!(
src,
" let mut items: Vec<_> = self.{id}().iter().collect();"
);
let _ = writeln!(src, " {sort};");
let _ = writeln!(src, " for (k, v) in items {{");
let key_write = text_map_key_write(kt, "k");
if value_is_msg {
let _ = writeln!(
src,
" pbrs::text::write_map_entry(out, indent, {name}, |out| {{ {key_write}; }}, |out| {{"
);
let _ = writeln!(src, " out.push_str(\"{{\\n\");");
let _ = writeln!(src, " v.write_text(out, indent + 4)?;");
let _ = writeln!(src, " pbrs::text::pad(out, indent + 2);");
let _ = writeln!(src, " out.push_str(\"}}\\n\");");
let _ = writeln!(src, " Ok(())");
let _ = writeln!(src, " }}, true)?;");
} else {
let val_write = vf
.map(|v| text_write_value(v, "v", true))
.unwrap_or_else(|| "out.push_str(&v.to_string())".into());
let _ = writeln!(
src,
" pbrs::text::write_map_entry(out, indent, {name}, |out| {{ {key_write}; }}, |out| {{ {val_write}; Ok(()) }}, false)?;"
);
}
let _ = writeln!(src, " }}");
let _ = writeln!(src, " }}");
} else if f.cardinality == Cardinality::Repeated {
if f.field_type == FieldType::Message {
let _ = writeln!(src, " for item in self.{id}() {{");
let _ = writeln!(src, " pbrs::text::pad(out, indent);");
let _ = writeln!(src, " out.push_str({name});");
let _ = writeln!(src, " out.push_str(\" {{\\n\");");
let _ = writeln!(src, " item.write_text(out, indent + 2)?;");
let _ = writeln!(src, " pbrs::text::pad(out, indent);");
let _ = writeln!(src, " out.push_str(\"}}\\n\");");
let _ = writeln!(src, " }}");
} else {
let _ = writeln!(src, " for item in self.{id}() {{");
let _ = writeln!(
src,
" {};",
text_write_named(f, &name, "item", true)
);
let _ = writeln!(src, " }}");
}
} else if f.field_type == FieldType::Message {
let _ = writeln!(src, " if self.has_{m}() {{");
let _ = writeln!(src, " pbrs::text::pad(out, indent);");
let _ = writeln!(src, " out.push_str({name});");
let _ = writeln!(src, " out.push_str(\" {{\\n\");");
let _ = writeln!(src, " self.{id}().write_text(out, indent + 2)?;");
let _ = writeln!(src, " pbrs::text::pad(out, indent);");
let _ = writeln!(src, " out.push_str(\"}}\\n\");");
let _ = writeln!(src, " }}");
} else {
let _ = writeln!(src, " if {} {{", typed_present_expr(f, &getter));
let _ = writeln!(
src,
" {};",
text_write_named(f, &name, &getter, false)
);
let _ = writeln!(src, " }}");
}
}
let _ = writeln!(src, " Ok(())");
let _ = writeln!(src, " }}");
}
fn emit_from_text_value(src: &mut String, desc: &MessageDescriptor) {
let _ = writeln!(
src,
" fn from_text_value(fields: &[(String, pbrs::text::TextValue)]) -> Result<Self, ParseError> {{"
);
let _ = writeln!(src, " let mut msg = Self::new();");
let _ = writeln!(src, " for (key, val) in fields {{");
let _ = writeln!(src, " match key.as_str() {{");
for f in desc.fields.values() {
let id = field_id(f);
let m = field_raw(f);
let name = rust_str(&f.name);
let _ = writeln!(src, " {name} => {{");
if f.is_map {
let kt = map_key_ty(f);
let vf = map_value_field(f);
let key_decode = match kt {
FieldType::String => "ev.as_str()?".to_string(),
FieldType::Bool => "ev.as_bool()?".to_string(),
FieldType::Int64 | FieldType::Sint64 | FieldType::Sfixed64 => {
"ev.as_i64()?".to_string()
}
FieldType::Uint32 | FieldType::Fixed32 => "ev.as_u32()?".to_string(),
FieldType::Uint64 | FieldType::Fixed64 => "ev.as_u64()?".to_string(),
_ => "ev.as_i32()?".to_string(),
};
let val_decode = match vf {
Some(v) if v.field_type == FieldType::Bytes => "ev.as_bytes()?.to_vec()".into(),
Some(v) => text_decode_leaf(v, "ev"),
None => "ev.as_i32()?".into(),
};
let _ = writeln!(src, " let entry = val.as_message()?;");
let _ = writeln!(
src,
" let mut k = {};",
text_map_key_default(kt)
);
let _ = writeln!(
src,
" let mut v = {};",
vf.map(text_map_val_default)
.unwrap_or_else(|| "0i32".into())
);
let _ = writeln!(src, " for (ek, ev) in entry {{");
let _ = writeln!(src, " match ek.as_str() {{");
let _ = writeln!(
src,
" \"key\" => k = {key_decode},"
);
let _ = writeln!(
src,
" \"value\" => v = {val_decode},"
);
let _ = writeln!(
src,
" _ => return Err(ParseError::owned(format!(\"unknown field {{ek}}\"))),"
);
let _ = writeln!(src, " }}");
let _ = writeln!(src, " }}");
let _ = writeln!(src, " msg.{id}_mut().insert(k, v);");
} else if f.cardinality == Cardinality::Repeated {
let item = text_decode_leaf(f, "item");
let one = text_decode_leaf(f, "val");
let _ = writeln!(
src,
" if let Some(items) = val.as_list() {{"
);
let _ = writeln!(src, " for item in items {{");
let _ = writeln!(
src,
" msg.{id}_mut().push({item});"
);
let _ = writeln!(src, " }}");
let _ = writeln!(src, " }} else {{");
let _ = writeln!(src, " msg.{id}_mut().push({one});");
let _ = writeln!(src, " }}");
} else if f.field_type == FieldType::Message {
let ty = scalar_type(f);
let _ = writeln!(
src,
" msg.{id}_mut().merge_from({ty}::from_text_value(val.as_message()?)?);"
);
} else {
let _ = writeln!(
src,
" msg.set_{m}({});",
text_decode_leaf(f, "val")
);
}
let _ = writeln!(src, " }}");
}
let _ = writeln!(
src,
" _ => return Err(ParseError::owned(format!(\"unknown field {{key}}\"))),"
);
let _ = writeln!(src, " }}");
let _ = writeln!(src, " }}");
let _ = writeln!(src, " Ok(msg)");
let _ = writeln!(src, " }}");
}
fn emit_dynamic_json(src: &mut String, full_name: &str) {
let _ = writeln!(
src,
" pub fn to_json(&self) -> Result<String, SerializeError> {{"
);
let _ = writeln!(src, " let b = pbrs::Serialize::serialize(self).map_err(|_| SerializeError::new(\"json\"))?;");
let _ = writeln!(src, " let pool = generated_pool();");
let _ = writeln!(
src,
" let desc = pool.get_message(\"{full_name}\").ok_or_else(|| SerializeError::new(\"missing desc\"))?;"
);
let _ = writeln!(src, " pbrs::DynamicMessage::parse_with_pool(desc, Some(pool), &b).map_err(|_| SerializeError::new(\"json\"))?.to_json()");
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" pub fn from_json(json: &str) -> Result<Self, ParseError> {{ Self::from_json_ignore(json, false) }}"
);
let _ = writeln!(
src,
" pub fn from_json_ignore(json: &str, ignore: bool) -> Result<Self, ParseError> {{"
);
let _ = writeln!(src, " let pool = generated_pool();");
let _ = writeln!(
src,
" let desc = pool.get_message(\"{full_name}\").ok_or_else(|| ParseError::owned(\"missing desc\".into()))?;"
);
let _ = writeln!(src, " let d = pbrs::DynamicMessage::from_json_with_pool(desc, Some(pool), json, ignore)?;");
let _ = writeln!(src, " let b = pbrs::Serialize::serialize(&d).map_err(|e| ParseError::owned(e.to_string()))?;");
let _ = writeln!(src, " <Self as pbrs::Parse>::parse(&b)");
let _ = writeln!(src, " }}");
}
fn emit_dynamic_text(src: &mut String, full_name: &str) {
let _ = writeln!(
src,
" pub fn to_text(&self) -> Result<String, SerializeError> {{"
);
let _ = writeln!(src, " let b = pbrs::Serialize::serialize(self).map_err(|_| SerializeError::new(\"text\"))?;");
let _ = writeln!(src, " let pool = generated_pool();");
let _ = writeln!(
src,
" let desc = pool.get_message(\"{full_name}\").ok_or_else(|| SerializeError::new(\"missing desc\"))?;"
);
let _ = writeln!(src, " pbrs::DynamicMessage::parse_with_pool(desc, Some(pool), &b).map_err(|_| SerializeError::new(\"text\"))?.to_text()");
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" pub fn to_text_with_unknown(&self) -> Result<String, SerializeError> {{"
);
let _ = writeln!(src, " let b = pbrs::Serialize::serialize(self).map_err(|_| SerializeError::new(\"text\"))?;");
let _ = writeln!(src, " let pool = generated_pool();");
let _ = writeln!(
src,
" let desc = pool.get_message(\"{full_name}\").ok_or_else(|| SerializeError::new(\"missing desc\"))?;"
);
let _ = writeln!(src, " pbrs::DynamicMessage::parse_with_pool(desc, Some(pool), &b).map_err(|_| SerializeError::new(\"text\"))?.to_text_with_unknown()");
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" pub fn from_text(text: &str) -> Result<Self, ParseError> {{"
);
let _ = writeln!(src, " let pool = generated_pool();");
let _ = writeln!(
src,
" let desc = pool.get_message(\"{full_name}\").ok_or_else(|| ParseError::owned(\"missing desc\".into()))?;"
);
let _ = writeln!(
src,
" let d = pbrs::DynamicMessage::from_text_with_pool(desc, Some(pool), text)?;"
);
let _ = writeln!(src, " let b = pbrs::Serialize::serialize(&d).map_err(|e| ParseError::owned(e.to_string()))?;");
let _ = writeln!(src, " <Self as pbrs::Parse>::parse(&b)");
let _ = writeln!(src, " }}");
}
fn wire_const(ty: FieldType) -> &'static str {
match ty {
FieldType::Fixed64 | FieldType::Sfixed64 | FieldType::Double => "pbrs::rt::WIRE_I64",
FieldType::Fixed32 | FieldType::Sfixed32 | FieldType::Float => "pbrs::rt::WIRE_I32",
FieldType::String | FieldType::Bytes | FieldType::Message | FieldType::Group => {
"pbrs::rt::WIRE_LEN"
}
_ => "pbrs::rt::WIRE_VARINT",
}
}
fn implicit_present(path: &str, ty: FieldType) -> String {
match ty {
FieldType::Bool => path.to_string(),
FieldType::Float | FieldType::Double => format!("{path}.to_bits() != 0"),
FieldType::String | FieldType::Bytes => format!("!{path}.is_empty()"),
_ => format!("{path} != 0"),
}
}
fn map_key_ty(field: &FieldDescriptor) -> FieldType {
field
.message
.as_ref()
.and_then(|e| e.field(1))
.map(|f| f.field_type)
.unwrap_or(FieldType::String)
}
fn map_val_ty(field: &FieldDescriptor) -> FieldType {
field
.message
.as_ref()
.and_then(|e| e.field(2))
.map(|f| f.field_type)
.unwrap_or(FieldType::Int32)
}
fn emit_codec(src: &mut String, desc: &MessageDescriptor) {
let required: Vec<_> = desc
.fields
.values()
.filter(|f| f.cardinality == Cardinality::Required)
.collect();
let _ = writeln!(
src,
" #[inline(always)] fn check_required(&self) -> Result<(), ParseError> {{"
);
for f in &required {
let id = field_id(f);
if is_option(f) {
let _ = writeln!(
src,
" if self.{id}.is_none() {{ return Err(ParseError::new(\"missing required field\")); }}"
);
}
}
let _ = writeln!(src, " Ok(())");
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" fn merge_bytes(&mut self, data: &[u8], depth: u32) -> Result<(), ParseError> {{ if data.is_empty() {{ return self.check_required(); }} let mut pos = 0; let mut wire = None; self.merge_inner(data, &mut wire, &mut pos, depth, true, None) }}"
);
let _ = writeln!(
src,
" fn merge_bytes_dont_enforce(&mut self, data: &[u8], depth: u32) -> Result<(), ParseError> {{ if data.is_empty() {{ return Ok(()); }} let mut pos = 0; let mut wire = None; self.merge_inner(data, &mut wire, &mut pos, depth, false, None) }}"
);
let _ = writeln!(
src,
" fn merge_group(&mut self, data: &[u8], wire: &mut Option<pbrs::rt::Wire>, pos: &mut usize, num: u32, depth: u32) -> Result<(), ParseError> {{ self.merge_inner(data, wire, pos, depth, false, Some(num)) }}"
);
let _ = writeln!(
src,
" fn merge_inner(&mut self, data: &[u8], wire: &mut Option<pbrs::rt::Wire>, pos: &mut usize, depth: u32, enforce: bool, until: Option<u32>) -> Result<(), ParseError> {{"
);
let _ = writeln!(
src,
" if depth > pbrs::RECURSION_LIMIT {{ return Err(ParseError::new(\"recursion limit exceeded\")); }} let _ = enforce;"
);
let _ = writeln!(src, " self.cached_size.dirty();");
let lights: Vec<_> = desc
.fields
.values()
.filter(|f| is_light_merge_field(f))
.collect();
let heavies: Vec<_> = desc
.fields
.values()
.filter(|f| !is_light_merge_field(f))
.collect();
let split_heavy =
!desc.message_set_wire_format && (1..=4).contains(&lights.len()) && !heavies.is_empty();
let _ = writeln!(src, " while *pos < data.len() {{");
let _ = writeln!(
src,
" let (n, w) = pbrs::rt::decode_tag(data, pos)?;"
);
let _ = writeln!(src, " if let Some(g) = until {{ if w == pbrs::rt::WIRE_EGROUP {{ if n != g {{ return Err(ParseError::new(\"mismatched end-group\")); }} return Ok(()); }} }}");
if split_heavy {
for f in &lights {
let _ = writeln!(src, " if n == {} {{", f.number);
let _ = writeln!(src, " match w {{");
emit_merge_arm(src, desc, f);
let _ = writeln!(
src,
" _ => self.unknown.fields.push(pbrs::rt::capture_unknown(data, pos, n, w)?),"
);
let _ = writeln!(src, " }}");
let _ = writeln!(src, " continue;");
let _ = writeln!(src, " }}");
}
let _ = writeln!(
src,
" self.merge_heavy(n, w, data, wire, pos, depth)?;"
);
} else {
let _ = writeln!(src, " match n {{");
if desc.message_set_wire_format {
emit_message_set_merge(src, desc);
}
for f in desc.fields.values() {
if desc.message_set_wire_format && f.number == 1 {
continue;
}
let _ = writeln!(src, " {} => match w {{", f.number);
emit_merge_arm(src, desc, f);
let _ = writeln!(
src,
" _ => self.unknown.fields.push(pbrs::rt::capture_unknown(data, pos, n, w)?),"
);
let _ = writeln!(src, " }}");
}
let _ = writeln!(
src,
" _ => self.unknown.fields.push(pbrs::rt::capture_unknown(data, pos, n, w)?),"
);
let _ = writeln!(src, " }}");
}
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" if until.is_some() {{ return Err(ParseError::new(\"truncated group\")); }}"
);
let _ = writeln!(src, " if enforce {{ self.check_required()?; }}");
let _ = writeln!(src, " Ok(())");
let _ = writeln!(src, " }}");
if split_heavy {
let _ = writeln!(src, " #[inline(never)]");
let _ = writeln!(
src,
" fn merge_heavy(&mut self, n: u32, w: u32, data: &[u8], wire: &mut Option<pbrs::rt::Wire>, pos: &mut usize, depth: u32) -> Result<(), ParseError> {{"
);
let _ = writeln!(src, " match n {{");
for f in &heavies {
let _ = writeln!(src, " {} => match w {{", f.number);
emit_merge_arm(src, desc, f);
let _ = writeln!(
src,
" _ => self.unknown.fields.push(pbrs::rt::capture_unknown(data, pos, n, w)?),"
);
let _ = writeln!(src, " }}");
}
let _ = writeln!(
src,
" _ => self.unknown.fields.push(pbrs::rt::capture_unknown(data, pos, n, w)?),"
);
let _ = writeln!(src, " }}");
let _ = writeln!(src, " Ok(())");
let _ = writeln!(src, " }}");
}
let _ = writeln!(
src,
" fn validate_inner(wire: &pbrs::rt::Wire, pos: &mut usize, depth: u32) -> Result<(), ParseError> {{ Self::validate_until(wire, pos, depth, None) }}"
);
let _ = writeln!(
src,
" fn validate_until(wire: &pbrs::rt::Wire, pos: &mut usize, depth: u32, until: Option<u32>) -> Result<(), ParseError> {{"
);
let _ = writeln!(
src,
" if depth > pbrs::RECURSION_LIMIT {{ return Err(ParseError::new(\"recursion limit exceeded\")); }}"
);
let _ = writeln!(src, " let data = wire.as_slice();");
if !required.is_empty() {
let _ = writeln!(src, " let mut seen = 0u64;");
}
let _ = writeln!(src, " while *pos < data.len() {{");
let _ = writeln!(
src,
" let (n, w) = pbrs::rt::decode_tag(data, pos)?;"
);
let _ = writeln!(src, " if let Some(g) = until {{ if w == pbrs::rt::WIRE_EGROUP {{ if n != g {{ return Err(ParseError::new(\"mismatched end-group\")); }} return Ok(()); }} }}");
let _ = writeln!(src, " match n {{");
for f in desc.fields.values() {
let bit = required.iter().position(|r| r.number == f.number);
let _ = writeln!(src, " {} => match w {{", f.number);
emit_validate_arm(src, f, bit);
let _ = writeln!(
src,
" _ => pbrs::rt::skip_field(data, pos, w)?,"
);
let _ = writeln!(src, " }}");
}
let _ = writeln!(
src,
" _ => pbrs::rt::skip_field(data, pos, w)?,"
);
let _ = writeln!(src, " }}");
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" if until.is_some() {{ return Err(ParseError::new(\"truncated group\")); }}"
);
if !required.is_empty() {
let all = (1u64 << required.len()) - 1;
let _ = writeln!(
src,
" if seen != {all} {{ return Err(ParseError::new(\"missing required field\")); }}"
);
}
let _ = writeln!(src, " Ok(())");
let _ = writeln!(src, " }}");
let _ = writeln!(src, " fn compute_size(&self) -> u64 {{");
let _ = writeln!(
src,
" if let Some(n) = self.cached_size.get() {{ return n; }}"
);
let _ = writeln!(src, " let mut n = self.unknown.encoded_len();");
if desc.message_set_wire_format {
emit_message_set_size(src, desc);
} else {
for f in desc.fields.values().filter(|f| stored_hot(desc, f)) {
emit_size(src, f, "self");
}
if uses_cold_storage(desc) {
let _ = writeln!(src, " if let Some(c) = self.cold.as_deref() {{");
for f in desc.fields.values().filter(|f| stored_cold(desc, f)) {
emit_size(src, f, "c");
}
let _ = writeln!(src, " }}");
}
}
let _ = writeln!(src, " self.cached_size.set(n);");
let _ = writeln!(src, " n");
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" fn write_to(&self, out: &mut impl pbrs::rt::WireOut) {{"
);
if desc.message_set_wire_format {
emit_message_set_write(src, desc);
} else {
for f in desc.fields.values().filter(|f| stored_hot(desc, f)) {
emit_write(src, f, "self");
}
if uses_cold_storage(desc) {
let _ = writeln!(src, " if let Some(c) = self.cold.as_deref() {{");
for f in desc.fields.values().filter(|f| stored_cold(desc, f)) {
emit_write(src, f, "c");
}
let _ = writeln!(src, " }}");
}
}
let _ = writeln!(src, " self.unknown.encode(out);");
let _ = writeln!(src, " }}");
}
fn emit_message_set_merge(src: &mut String, desc: &MessageDescriptor) {
let _ = writeln!(
src,
" 1 => match w {{ pbrs::rt::WIRE_SGROUP | pbrs::rt::WIRE_LEN => {{"
);
let _ = writeln!(
src,
" let mut type_id = 0u32; let mut payload: Vec<u8> = Vec::new();"
);
let _ = writeln!(src, " if w == pbrs::rt::WIRE_LEN {{ let inner = pbrs::rt::read_len_bytes(data, pos)?; let mut p = 0; while p < inner.len() {{ let (n, ww) = pbrs::rt::decode_tag(inner, &mut p)?; match (n, ww) {{ (2, pbrs::rt::WIRE_VARINT) => type_id = pbrs::rt::decode_varint(inner, &mut p)? as u32, (3, pbrs::rt::WIRE_LEN) => payload = pbrs::rt::read_len_bytes(inner, &mut p)?.to_vec(), _ => pbrs::rt::skip_field(inner, &mut p, ww)?, }} }} }} else {{ loop {{ let (n, ww) = pbrs::rt::decode_tag(data, pos)?; if ww == pbrs::rt::WIRE_EGROUP && n == 1 {{ break; }} match (n, ww) {{ (2, pbrs::rt::WIRE_VARINT) => type_id = pbrs::rt::decode_varint(data, pos)? as u32, (3, pbrs::rt::WIRE_LEN) => payload = pbrs::rt::read_len_bytes(data, pos)?.to_vec(), _ => pbrs::rt::skip_field(data, pos, ww)?, }} }} }}");
let _ = writeln!(src, " match type_id {{");
for f in desc.fields.values() {
let id = field_id(f);
let t = scalar_type(f);
let num = f.number;
if is_lazy_msg(f) {
let _ = writeln!(src, " {num} => {{ if self.{id}.is_some() {{ self.{id}.get_or_insert().merge_bytes(&payload, depth + 1)?; }} else {{ let mut inner = {t}::default(); inner.merge_bytes(&payload, depth + 1)?; self.{id} = pbrs::rt::LazyMsg::from_owned(inner); }} }}");
} else {
let _ = writeln!(src, " {num} => {{ match &mut self.{id} {{ Some(existing) => existing.merge_bytes(&payload, depth + 1)?, None => {{ let mut inner = {t}::default(); inner.merge_bytes(&payload, depth + 1)?; self.{id} = Some(Box::new(inner)); }} }} }}");
}
}
let _ = writeln!(src, " _ => {{ let mut u = pbrs::UnknownFields::default(); u.fields.push(pbrs::rt::UnknownField::Varint {{ number: 2, value: u64::from(type_id) }}); u.fields.push(pbrs::rt::UnknownField::LengthDelimited {{ number: 3, value: payload }}); self.unknown.fields.push(pbrs::rt::UnknownField::Group {{ number: 1, fields: u }}); }}");
let _ = writeln!(src, " }}");
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" _ => self.unknown.fields.push(pbrs::rt::capture_unknown(data, pos, n, w)?),"
);
let _ = writeln!(src, " }}");
}
fn emit_message_set_size(src: &mut String, desc: &MessageDescriptor) {
for f in desc.fields.values() {
let id = field_id(f);
let num = f.number;
let _ = writeln!(src, " if let Some(m) = self.{id}.as_deref() {{ let inner = m.compute_size(); let item = pbrs::rt::tag_len(2, pbrs::rt::WIRE_VARINT) + pbrs::rt::varint_len({num} as u64) + pbrs::rt::key_len_value_len(3, inner); n += pbrs::rt::tag_len(1, pbrs::rt::WIRE_SGROUP) + item + pbrs::rt::tag_len(1, pbrs::rt::WIRE_EGROUP); }}");
}
}
fn emit_message_set_write(src: &mut String, desc: &MessageDescriptor) {
for f in desc.fields.values() {
let id = field_id(f);
let num = f.number;
let _ = writeln!(src, " if let Some(m) = self.{id}.as_deref() {{ pbrs::rt::encode_tag(out, 1, pbrs::rt::WIRE_SGROUP); pbrs::rt::encode_tag(out, 2, pbrs::rt::WIRE_VARINT); pbrs::rt::encode_varint(out, {num} as u64); pbrs::rt::encode_len_header(out, 3, m.compute_size()); m.write_to(out); pbrs::rt::encode_tag(out, 1, pbrs::rt::WIRE_EGROUP); }}");
}
}
fn read_scalar_expr(ty: FieldType, buf: &str, pos: &str) -> String {
match ty {
FieldType::Double => format!("f64::from_bits(pbrs::rt::read_fixed64({buf}, {pos})?)"),
FieldType::Float => format!("f32::from_bits(pbrs::rt::read_fixed32({buf}, {pos})?)"),
FieldType::Fixed64 => format!("pbrs::rt::read_fixed64({buf}, {pos})?"),
FieldType::Sfixed64 => format!("pbrs::rt::read_fixed64({buf}, {pos})? as i64"),
FieldType::Fixed32 => format!("pbrs::rt::read_fixed32({buf}, {pos})?"),
FieldType::Sfixed32 => format!("pbrs::rt::read_fixed32({buf}, {pos})? as i32"),
FieldType::Sint32 => {
format!("pbrs::rt::decode_zigzag32(pbrs::rt::decode_varint({buf}, {pos})?)")
}
FieldType::Sint64 => {
format!("pbrs::rt::decode_zigzag64(pbrs::rt::decode_varint({buf}, {pos})?)")
}
FieldType::Bool => format!("pbrs::rt::decode_varint({buf}, {pos})? != 0"),
FieldType::Int64 => format!("pbrs::rt::decode_varint({buf}, {pos})? as i64"),
FieldType::Uint64 => format!("pbrs::rt::decode_varint({buf}, {pos})?"),
FieldType::Uint32 => format!("pbrs::rt::decode_varint({buf}, {pos})? as u32"),
_ => format!("pbrs::rt::decode_varint({buf}, {pos})? as i32"),
}
}
fn emit_validate_arm(src: &mut String, f: &FieldDescriptor, req_bit: Option<usize>) {
let num = f.number;
let mark = if let Some(b) = req_bit {
format!(" seen |= 1 << {b};")
} else {
String::new()
};
if f.is_map {
let _ = writeln!(src, " pbrs::rt::WIRE_LEN => {{ let (s, e) = pbrs::rt::read_len_span(data, pos)?; let mut ip = 0; let w = wire.window(s, e); let d = w.as_slice(); while ip < d.len() {{ let (_, ww) = pbrs::rt::decode_tag(d, &mut ip)?; pbrs::rt::skip_field(d, &mut ip, ww)?; }}{mark} }}");
return;
}
if f.cardinality == Cardinality::Repeated {
if f.field_type == FieldType::String {
let utf = if f.utf8_validate {
"pbrs::rt::require_utf8(&data[s..e])?;"
} else {
""
};
let _ = writeln!(src, " pbrs::rt::WIRE_LEN => {{ let (s, e) = pbrs::rt::read_len_span(data, pos)?; {utf}{mark} }}");
} else if f.field_type == FieldType::Bytes {
let _ = writeln!(src, " pbrs::rt::WIRE_LEN => {{ pbrs::rt::read_len_span(data, pos)?;{mark} }}");
} else if f.field_type == FieldType::Message {
let t = scalar_type(f);
let _ = writeln!(src, " pbrs::rt::WIRE_LEN => {{ let (s, e) = pbrs::rt::read_len_span(data, pos)?; let mut ip = 0; {t}::validate_inner(&wire.window(s, e), &mut ip, depth + 1)?;{mark} }}");
} else if f.field_type == FieldType::Group || f.delimited {
let t = scalar_type(f);
let _ = writeln!(src, " pbrs::rt::WIRE_SGROUP => {{ {t}::validate_until(wire, pos, depth + 1, Some({num}))?;{mark} }}");
} else if f.field_type.is_packable() {
let packed_ty = packed_storage_ty(f);
let unpacked = read_scalar_expr(f.field_type, "data", "pos");
let packed_wire = wire_const(f.field_type);
let _ = writeln!(src, " pbrs::rt::WIRE_LEN => {{ let (s, e) = pbrs::rt::read_len_span(data, pos)?; {packed_ty}::validate_bytes(&data[s..e])?;{mark} }}");
let _ = writeln!(
src,
" {packed_wire} => {{ let _ = {unpacked};{mark} }}"
);
} else {
let unpacked = read_scalar_expr(f.field_type, "data", "pos");
let w = wire_const(f.field_type);
let _ = writeln!(
src,
" {w} => {{ let _ = {unpacked};{mark} }}"
);
}
return;
}
if f.field_type == FieldType::Message {
let t = scalar_type(f);
if f.field_type == FieldType::Group || f.delimited {
let _ = writeln!(src, " pbrs::rt::WIRE_SGROUP => {{ {t}::validate_until(wire, pos, depth + 1, Some({num}))?;{mark} }}");
} else {
let _ = writeln!(src, " pbrs::rt::WIRE_LEN => {{ let (s, e) = pbrs::rt::read_len_span(data, pos)?; let mut ip = 0; {t}::validate_inner(&wire.window(s, e), &mut ip, depth + 1)?;{mark} }}");
}
return;
}
if f.field_type == FieldType::Group || f.delimited {
let t = scalar_type(f);
let _ = writeln!(src, " pbrs::rt::WIRE_SGROUP => {{ {t}::validate_until(wire, pos, depth + 1, Some({num}))?;{mark} }}");
return;
}
if f.field_type == FieldType::String {
let utf = if f.utf8_validate {
"pbrs::rt::require_utf8(&data[s..e])?;"
} else {
""
};
let _ = writeln!(src, " pbrs::rt::WIRE_LEN => {{ let (s, e) = pbrs::rt::read_len_span(data, pos)?; {utf}{mark} }}");
return;
}
if f.field_type == FieldType::Bytes {
let _ = writeln!(src, " pbrs::rt::WIRE_LEN => {{ pbrs::rt::read_len_span(data, pos)?;{mark} }}");
return;
}
let expr = read_scalar_expr(f.field_type, "data", "pos");
let w = wire_const(f.field_type);
let _ = writeln!(src, " {w} => {{ let _ = {expr};{mark} }}");
}
fn lazy_str_from_parse(f: &FieldDescriptor) -> &'static str {
if f.utf8_validate {
"pbrs::rt::LazyStr::from_parse_span(wire, data, s, e)?"
} else {
"pbrs::rt::LazyStr::from_parse_span_unchecked(wire, data, s, e)"
}
}
/// Same-tag run for repeated length-delimited items (strings/bytes).
/// Reserve from remaining bytes so `tags_32` does not grow the Vec 5×.
fn emit_repeated_len_run(src: &mut String, num: u32, st: &str, push: &str) {
let _ = writeln!(
src,
" pbrs::rt::WIRE_LEN => {{ let (s, e) = pbrs::rt::read_len_span(data, pos)?; let rest = data.len().saturating_sub(e); if rest > 2 {{ {st}.reserve(1 + rest / (e - s + 2).max(1)); }} {st}.push({push}); while *pos < data.len() {{ let save = *pos; match pbrs::rt::decode_tag(data, pos) {{ Ok((n2, w2)) if n2 == {num} && w2 == pbrs::rt::WIRE_LEN => {{ let (s, e) = pbrs::rt::read_len_span(data, pos)?; {st}.push({push}); }} Ok(_) => {{ *pos = save; break; }} Err(e) => return Err(e), }} }} }}"
);
}
fn emit_merge_arm(src: &mut String, desc: &MessageDescriptor, f: &FieldDescriptor) {
let st = store_mut(desc, f);
let num = f.number;
if f.is_map {
let _ = writeln!(src, " pbrs::rt::WIRE_LEN => {{");
let _ = writeln!(
src,
" let (s, e) = pbrs::rt::read_len_span(data, pos)?;"
);
let _ = writeln!(
src,
" let (kk, vv) = decode_map_entry_{}_{}_{num}(&pbrs::rt::Wire::ensure(wire, data).window(s, e), depth + 1)?;",
rust_ident(&desc.full_name),
field_id(f).replace("r#", "")
);
let _ = writeln!(src, " {st}.push_entry(kk, vv);");
let _ = writeln!(src, " }}");
return;
}
if f.cardinality == Cardinality::Repeated {
if f.field_type == FieldType::String {
emit_repeated_len_run(src, num, &st, lazy_str_from_parse(f));
} else if f.field_type == FieldType::Bytes {
emit_repeated_len_run(
src,
num,
&st,
"pbrs::rt::LazyBytes::from_wire(pbrs::rt::Wire::ensure(wire, data).window(s, e))",
);
} else if f.field_type == FieldType::Message || f.field_type == FieldType::Group {
let t = scalar_type(f);
if f.field_type == FieldType::Group || f.delimited {
let _ = writeln!(src, " pbrs::rt::WIRE_SGROUP => {{ let mut inner = {t}::default(); inner.merge_group(data, wire, pos, {num}, depth + 1)?; {st}.push(inner); }}");
} else {
let _ = writeln!(
src,
" pbrs::rt::WIRE_LEN => {{ let (s, e) = pbrs::rt::read_len_span(data, pos)?; let rest = data.len().saturating_sub(e); if rest > 2 {{ {st}.reserve(1 + rest / (e - s + 4).max(1)); }} let mut inner = {t}::default(); let mut ip = 0; let mut sw = None; inner.merge_inner(&data[s..e], &mut sw, &mut ip, depth + 1, true, None)?; {st}.push(inner); while *pos < data.len() {{ let save = *pos; match pbrs::rt::decode_tag(data, pos) {{ Ok((n2, w2)) if n2 == {num} && w2 == pbrs::rt::WIRE_LEN => {{ let (s, e) = pbrs::rt::read_len_span(data, pos)?; let mut inner = {t}::default(); let mut ip = 0; let mut sw = None; inner.merge_inner(&data[s..e], &mut sw, &mut ip, depth + 1, true, None)?; {st}.push(inner); }} Ok(_) => {{ *pos = save; break; }} Err(e) => return Err(e), }} }} }}"
);
}
} else if f.field_type.is_packable() {
let unpacked = read_scalar_expr(f.field_type, "data", "pos");
let packed_wire = wire_const(f.field_type);
if f.packed {
if is_memcpy_packed(f) {
// Payload-only Arc. Do not Arc the parent message and do
// not eager-decode the Vec on parse.
let _ = writeln!(src, " pbrs::rt::WIRE_LEN => {{ let (s, e) = pbrs::rt::read_len_span(data, pos)?; {st}.append_wire(pbrs::rt::Wire::from_slice(&data[s..e]))?; }}");
} else {
let _ = writeln!(src, " pbrs::rt::WIRE_LEN => {{ let (s, e) = pbrs::rt::read_len_span(data, pos)?; {st}.append_wire(pbrs::rt::Wire::ensure(wire, data).window(s, e))?; }}");
}
} else {
let expr = read_scalar_expr(f.field_type, "p", "&mut i");
let _ = writeln!(src, " pbrs::rt::WIRE_LEN => {{ let p = pbrs::rt::read_len_bytes(data, pos)?; let mut i = 0; while i < p.len() {{ {st}.push({expr}); }} }}");
}
let _ = writeln!(
src,
" {packed_wire} => {{ {st}.push({unpacked}); let rest = data.len().saturating_sub(*pos); if rest > 2 {{ {st}.reserve(rest / 3); }} while *pos < data.len() {{ let save = *pos; match pbrs::rt::decode_tag(data, pos) {{ Ok((n2, w2)) if n2 == {num} && w2 == {packed_wire} => {st}.push({unpacked}), Ok(_) => {{ *pos = save; break; }} Err(e) => return Err(e), }} }} }}"
);
} else {
let unpacked = read_scalar_expr(f.field_type, "data", "pos");
let w = wire_const(f.field_type);
let _ = writeln!(src, " {w} => {st}.push({unpacked}),");
}
return;
}
if f.field_type == FieldType::Message || f.field_type == FieldType::Group {
let t = scalar_type(f);
if f.field_type == FieldType::Group || f.delimited {
let _ = writeln!(src, " pbrs::rt::WIRE_SGROUP => {{");
emit_oneof_clear(src, desc, f);
let _ = writeln!(src, " match &mut {st} {{ Some(existing) => existing.merge_group(data, wire, pos, {num}, depth + 1)?, None => {{ let mut inner = {t}::default(); inner.merge_group(data, wire, pos, {num}, depth + 1)?; {st} = Some(Box::new(inner)); }} }}");
let _ = writeln!(src, " }}");
} else {
let _ = writeln!(src, " pbrs::rt::WIRE_LEN => {{");
emit_oneof_clear(src, desc, f);
let _ = writeln!(
src,
" let (s, e) = pbrs::rt::read_len_span(data, pos)?;"
);
if is_lazy_msg(f) {
let _ = writeln!(src, " if {st}.is_some() {{ let mut ip = 0; let mut sw = None; {st}.get_or_insert().merge_inner(&data[s..e], &mut sw, &mut ip, depth + 1, true, None)?; }} else {{ let mut ip = 0; {t}::validate_inner(&pbrs::rt::Wire::ensure(wire, data).window(s, e), &mut ip, depth + 1)?; {st} = pbrs::rt::LazyMsg::from_wire(pbrs::rt::Wire::ensure(wire, data).window(s, e)); }}");
} else {
let _ = writeln!(src, " match &mut {st} {{ Some(existing) => {{ let mut ip = 0; let mut sw = None; existing.merge_inner(&data[s..e], &mut sw, &mut ip, depth + 1, true, None)?; }} None => {{ let mut inner = {t}::default(); let mut ip = 0; let mut sw = None; inner.merge_inner(&data[s..e], &mut sw, &mut ip, depth + 1, true, None)?; {st} = Some(Box::new(inner)); }} }}");
}
let _ = writeln!(src, " }}");
}
return;
}
if f.field_type == FieldType::String {
let parse = lazy_str_from_parse(f);
let assign = if is_option(f) {
format!("{st} = Some(Box::new({parse}))")
} else {
format!("{st} = {parse}")
};
let _ = writeln!(src, " pbrs::rt::WIRE_LEN => {{");
emit_oneof_clear(src, desc, f);
let _ = writeln!(
src,
" let (s, e) = pbrs::rt::read_len_span(data, pos)?; {assign};"
);
let _ = writeln!(src, " }}");
return;
}
if f.field_type == FieldType::Bytes {
let assign = if is_option(f) {
format!("{st} = Some(Box::new(pbrs::rt::LazyBytes::from_wire(pbrs::rt::Wire::ensure(wire, data).window(s, e))))")
} else {
format!("{st} = pbrs::rt::LazyBytes::from_wire(pbrs::rt::Wire::ensure(wire, data).window(s, e))")
};
let _ = writeln!(src, " pbrs::rt::WIRE_LEN => {{");
emit_oneof_clear(src, desc, f);
let _ = writeln!(
src,
" let (s, e) = pbrs::rt::read_len_span(data, pos)?; {assign};"
);
let _ = writeln!(src, " }}");
return;
}
let expr = read_scalar_expr(f.field_type, "data", "pos");
let w = wire_const(f.field_type);
let assign = if is_option(f) && f.field_type == FieldType::Bool {
format!("{st} = pbrs::rt::OptBool::some({expr})")
} else if is_option(f) {
format!("{st} = Some({expr})")
} else {
format!("{st} = {expr}")
};
let _ = writeln!(src, " {w} => {{");
emit_oneof_clear(src, desc, f);
let _ = writeln!(src, " {assign};");
let _ = writeln!(src, " }}");
}
fn packed_len_expr(v: &str, ty: FieldType) -> String {
match ty {
FieldType::Fixed64 | FieldType::Sfixed64 | FieldType::Double => "8".into(),
FieldType::Fixed32 | FieldType::Sfixed32 | FieldType::Float => "4".into(),
FieldType::Sint32 => {
format!("pbrs::rt::varint_len(pbrs::rt::encode_zigzag32({v}))")
}
FieldType::Sint64 => {
format!("pbrs::rt::varint_len(pbrs::rt::encode_zigzag64({v}))")
}
FieldType::Bool => format!("pbrs::rt::varint_len(u64::from({v}))"),
_ => format!("pbrs::rt::varint_len(({v}) as u64)"),
}
}
fn write_packed_stmt(out: &str, v: &str, ty: FieldType) -> String {
match ty {
FieldType::Double => format!("({out}).extend_from_slice(&({v}).to_bits().to_le_bytes())"),
FieldType::Float => format!("({out}).extend_from_slice(&({v}).to_bits().to_le_bytes())"),
FieldType::Fixed64 => format!("({out}).extend_from_slice(&({v}).to_le_bytes())"),
FieldType::Sfixed64 => format!("({out}).extend_from_slice(&(({v}) as u64).to_le_bytes())"),
FieldType::Fixed32 => format!("({out}).extend_from_slice(&({v}).to_le_bytes())"),
FieldType::Sfixed32 => format!("({out}).extend_from_slice(&(({v}) as u32).to_le_bytes())"),
FieldType::Sint32 => {
format!("pbrs::rt::encode_varint({out}, pbrs::rt::encode_zigzag32({v}))")
}
FieldType::Sint64 => {
format!("pbrs::rt::encode_varint({out}, pbrs::rt::encode_zigzag64({v}))")
}
FieldType::Bool => format!("pbrs::rt::encode_varint({out}, u64::from({v}))"),
_ => format!("pbrs::rt::encode_varint({out}, {v} as u64)"),
}
}
fn emit_size(src: &mut String, f: &FieldDescriptor, p: &str) {
let id = field_id(f);
let fld = format!("{p}.{id}");
let num = f.number;
if f.is_map {
let _ = writeln!(src, " if !{fld}.is_empty() {{");
let _ = writeln!(src, " for (k, v) in {fld}.pairs() {{");
let key_sz = map_key_size(map_key_ty(f), "k");
let val_sz = map_val_size(map_val_ty(f), "v");
let _ = writeln!(src, " let inner = {key_sz} + {val_sz};");
let _ = writeln!(
src,
" n += pbrs::rt::key_len_value_len({num}, inner);"
);
let _ = writeln!(src, " }}");
let _ = writeln!(src, " }}");
return;
}
if f.cardinality == Cardinality::Repeated {
if f.field_type == FieldType::String || f.field_type == FieldType::Bytes {
let _ = writeln!(src, " for t in {fld}.iter() {{ n += pbrs::rt::key_len_value_len({num}, t.as_bytes().len() as u64); }}");
} else if f.field_type == FieldType::Group || f.delimited {
let _ = writeln!(src, " for t in {fld}.iter() {{ n += pbrs::rt::tag_len({num}, pbrs::rt::WIRE_SGROUP) + t.compute_size() + pbrs::rt::tag_len({num}, pbrs::rt::WIRE_EGROUP); }}");
} else if f.field_type == FieldType::Message {
let _ = writeln!(src, " for t in {fld}.iter() {{ n += pbrs::rt::key_len_value_len({num}, t.compute_size()); }}");
} else if f.packed && f.field_type.is_packable() {
let plen = packed_len_expr("*t", f.field_type);
let _ = writeln!(src, " if let Some(p) = {fld}.packed_bytes() {{ n += pbrs::rt::key_len_value_len({num}, p.len() as u64); }} else if !{fld}.is_empty() {{ let mut payload = 0u64; for t in {fld}.iter() {{ payload += {plen}; }} n += pbrs::rt::key_len_value_len({num}, payload); }}");
} else {
let plen = packed_len_expr("*t", f.field_type);
let w = wire_const(f.field_type);
let _ = writeln!(
src,
" for t in {fld}.iter() {{ n += pbrs::rt::tag_len({num}, {w}) + {plen}; }}"
);
}
return;
}
if f.field_type == FieldType::Group || f.delimited {
let _ = writeln!(src, " if let Some(m) = &{fld} {{ n += pbrs::rt::tag_len({num}, pbrs::rt::WIRE_SGROUP) + m.compute_size() + pbrs::rt::tag_len({num}, pbrs::rt::WIRE_EGROUP); }}");
return;
}
if f.field_type == FieldType::Message {
if is_lazy_msg(f) {
let _ = writeln!(src, " if let Some(p) = {fld}.wire_bytes() {{ n += pbrs::rt::key_len_value_len({num}, p.len() as u64); }} else if let Some(m) = {fld}.as_deref() {{ n += pbrs::rt::key_len_value_len({num}, m.compute_size()); }}");
} else {
let _ = writeln!(src, " if let Some(m) = &{fld} {{ n += pbrs::rt::key_len_value_len({num}, m.compute_size()); }}");
}
return;
}
if f.field_type == FieldType::String || f.field_type == FieldType::Bytes {
if is_option(f) {
let _ = writeln!(src, " if let Some(s) = &{fld} {{ n += pbrs::rt::key_len_value_len({num}, s.as_bytes().len() as u64); }}");
} else {
let _ = writeln!(src, " if !{fld}.is_empty() {{ n += pbrs::rt::key_len_value_len({num}, {fld}.as_bytes().len() as u64); }}");
}
return;
}
let sz = packed_len_expr("v", f.field_type);
let w = wire_const(f.field_type);
if is_option(f) && f.field_type == FieldType::Bool {
let _ = writeln!(
src,
" if let Some(v) = {fld}.get() {{ n += pbrs::rt::tag_len({num}, {w}) + {sz}; }}"
);
} else if is_option(f) {
let _ = writeln!(
src,
" if let Some(v) = {fld} {{ n += pbrs::rt::tag_len({num}, {w}) + {sz}; }}"
);
} else {
let pred = implicit_present(&fld, f.field_type);
let sz2 = packed_len_expr(&fld, f.field_type);
let _ = writeln!(
src,
" if {pred} {{ n += pbrs::rt::tag_len({num}, {w}) + {sz2}; }}"
);
}
}
fn map_key_size(ty: FieldType, var: &str) -> String {
match ty {
FieldType::String => {
format!("pbrs::rt::key_len_value_len(1, {var}.as_bytes().len() as u64)")
}
FieldType::Bool => format!(
"pbrs::rt::tag_len(1, pbrs::rt::WIRE_VARINT) + pbrs::rt::varint_len(u64::from(*{var}))"
),
FieldType::Fixed32 | FieldType::Sfixed32 => {
"pbrs::rt::tag_len(1, pbrs::rt::WIRE_I32) + 4".into()
}
FieldType::Fixed64 | FieldType::Sfixed64 => {
"pbrs::rt::tag_len(1, pbrs::rt::WIRE_I64) + 8".into()
}
FieldType::Sint32 => format!(
"pbrs::rt::tag_len(1, pbrs::rt::WIRE_VARINT) + pbrs::rt::varint_len(pbrs::rt::encode_zigzag32(*{var}))"
),
FieldType::Sint64 => format!(
"pbrs::rt::tag_len(1, pbrs::rt::WIRE_VARINT) + pbrs::rt::varint_len(pbrs::rt::encode_zigzag64(*{var}))"
),
FieldType::Uint32 => format!(
"pbrs::rt::tag_len(1, pbrs::rt::WIRE_VARINT) + pbrs::rt::varint_len(u64::from(*{var}))"
),
FieldType::Uint64 => format!(
"pbrs::rt::tag_len(1, pbrs::rt::WIRE_VARINT) + pbrs::rt::varint_len(*{var})"
),
_ => format!(
"pbrs::rt::tag_len(1, pbrs::rt::WIRE_VARINT) + pbrs::rt::varint_len((*{var}) as u64)"
),
}
}
fn map_val_size(ty: FieldType, var: &str) -> String {
match ty {
FieldType::String | FieldType::Bytes => {
format!("pbrs::rt::key_len_value_len(2, {var}.as_bytes().len() as u64)")
}
FieldType::Message | FieldType::Group => {
format!("pbrs::rt::key_len_value_len(2, {var}.compute_size())")
}
FieldType::Bool => {
format!("pbrs::rt::tag_len(2, pbrs::rt::WIRE_VARINT) + pbrs::rt::varint_len(u64::from(*{var}))")
}
FieldType::Float => "pbrs::rt::tag_len(2, pbrs::rt::WIRE_I32) + 4".into(),
FieldType::Double => "pbrs::rt::tag_len(2, pbrs::rt::WIRE_I64) + 8".into(),
FieldType::Fixed32 | FieldType::Sfixed32 => {
"pbrs::rt::tag_len(2, pbrs::rt::WIRE_I32) + 4".into()
}
FieldType::Fixed64 | FieldType::Sfixed64 => {
"pbrs::rt::tag_len(2, pbrs::rt::WIRE_I64) + 8".into()
}
FieldType::Sint32 => format!(
"pbrs::rt::tag_len(2, pbrs::rt::WIRE_VARINT) + pbrs::rt::varint_len(pbrs::rt::encode_zigzag32(*{var}))"
),
FieldType::Sint64 => format!(
"pbrs::rt::tag_len(2, pbrs::rt::WIRE_VARINT) + pbrs::rt::varint_len(pbrs::rt::encode_zigzag64(*{var}))"
),
FieldType::Uint32 => format!(
"pbrs::rt::tag_len(2, pbrs::rt::WIRE_VARINT) + pbrs::rt::varint_len(u64::from(*{var}))"
),
FieldType::Uint64 => format!(
"pbrs::rt::tag_len(2, pbrs::rt::WIRE_VARINT) + pbrs::rt::varint_len(*{var})"
),
_ => format!(
"pbrs::rt::tag_len(2, pbrs::rt::WIRE_VARINT) + pbrs::rt::varint_len((*{var}) as u64)"
),
}
}
fn emit_write(src: &mut String, f: &FieldDescriptor, p: &str) {
let id = field_id(f);
let fld = format!("{p}.{id}");
let num = f.number;
if f.is_map {
let kty = map_key_ty(f);
let vty = map_val_ty(f);
let _ = writeln!(src, " if !{fld}.is_empty() {{");
let _ = writeln!(src, " for (k, v) in {fld}.pairs() {{");
let _ = writeln!(
src,
" let inner = {} + {};",
map_key_size(kty, "k"),
map_val_size(vty, "v")
);
let _ = writeln!(src, " pbrs::rt::encode_tag(out, {num}, pbrs::rt::WIRE_LEN); pbrs::rt::encode_varint(out, inner);");
emit_map_key_write(src, kty);
emit_map_val_write(src, vty);
let _ = writeln!(src, " }}");
let _ = writeln!(src, " }}");
return;
}
if f.cardinality == Cardinality::Repeated {
if f.field_type == FieldType::String || f.field_type == FieldType::Bytes {
let _ = writeln!(src, " for t in {fld}.iter() {{ pbrs::rt::encode_len_field(out, {num}, t.as_bytes()); }}");
} else if f.field_type == FieldType::Group || f.delimited {
let _ = writeln!(src, " for t in {fld}.iter() {{ pbrs::rt::encode_tag(out, {num}, pbrs::rt::WIRE_SGROUP); t.write_to(out); pbrs::rt::encode_tag(out, {num}, pbrs::rt::WIRE_EGROUP); }}");
} else if f.field_type == FieldType::Message {
let _ = writeln!(src, " for t in {fld}.iter() {{ pbrs::rt::encode_len_header(out, {num}, t.compute_size()); t.write_to(out); }}");
} else if f.packed && f.field_type.is_packable() {
let plen = packed_len_expr("*t", f.field_type);
let stmt = write_packed_stmt("out", "*t", f.field_type);
let _ = writeln!(src, " if let Some(p) = {fld}.packed_bytes() {{ pbrs::rt::encode_len_header(out, {num}, p.len() as u64); out.extend_from_slice(p); }} else if !{fld}.is_empty() {{ let mut payload = 0u64; for t in {fld}.iter() {{ payload += {plen}; }} pbrs::rt::encode_len_header(out, {num}, payload); for t in {fld}.iter() {{ {stmt}; }} }}");
} else {
let w = wire_const(f.field_type);
let stmt = write_packed_stmt("out", "*t", f.field_type);
let _ = writeln!(src, " for t in {fld}.iter() {{ pbrs::rt::encode_tag(out, {num}, {w}); {stmt}; }}");
}
return;
}
if f.field_type == FieldType::Group || f.delimited {
let _ = writeln!(src, " if let Some(m) = &{fld} {{ pbrs::rt::encode_tag(out, {num}, pbrs::rt::WIRE_SGROUP); m.write_to(out); pbrs::rt::encode_tag(out, {num}, pbrs::rt::WIRE_EGROUP); }}");
return;
}
if f.field_type == FieldType::Message {
if is_lazy_msg(f) {
let _ = writeln!(src, " if let Some(p) = {fld}.wire_bytes() {{ pbrs::rt::encode_len_header(out, {num}, p.len() as u64); out.extend_from_slice(p); }} else if let Some(m) = {fld}.as_deref() {{ pbrs::rt::encode_len_header(out, {num}, m.compute_size()); m.write_to(out); }}");
} else {
let _ = writeln!(src, " if let Some(m) = &{fld} {{ pbrs::rt::encode_len_header(out, {num}, m.compute_size()); m.write_to(out); }}");
}
return;
}
if f.field_type == FieldType::String || f.field_type == FieldType::Bytes {
if is_option(f) {
let _ = writeln!(src, " if let Some(s) = &{fld} {{ pbrs::rt::encode_len_field(out, {num}, s.as_bytes()); }}");
} else {
let _ = writeln!(src, " if !{fld}.is_empty() {{ pbrs::rt::encode_len_field(out, {num}, {fld}.as_bytes()); }}");
}
return;
}
let w = wire_const(f.field_type);
if is_option(f) && f.field_type == FieldType::Bool {
let stmt = write_packed_stmt("out", "v", f.field_type);
let _ = writeln!(src, " if let Some(v) = {fld}.get() {{ pbrs::rt::encode_tag(out, {num}, {w}); {stmt}; }}");
} else if is_option(f) {
let stmt = write_packed_stmt("out", "v", f.field_type);
let _ = writeln!(
src,
" if let Some(v) = {fld} {{ pbrs::rt::encode_tag(out, {num}, {w}); {stmt}; }}"
);
} else {
let pred = implicit_present(&fld, f.field_type);
let stmt = write_packed_stmt("out", &fld, f.field_type);
let _ = writeln!(
src,
" if {pred} {{ pbrs::rt::encode_tag(out, {num}, {w}); {stmt}; }}"
);
}
}
fn emit_map_key_write(src: &mut String, ty: FieldType) {
emit_map_scalar_write(src, 1, "k", ty);
}
fn emit_map_val_write(src: &mut String, ty: FieldType) {
emit_map_scalar_write(src, 2, "v", ty);
}
fn emit_map_scalar_write(src: &mut String, n: u32, var: &str, ty: FieldType) {
match ty {
FieldType::String | FieldType::Bytes => {
let _ = writeln!(
src,
" pbrs::rt::encode_len_field(out, {n}, {var}.as_bytes());"
);
}
FieldType::Message | FieldType::Group => {
let _ = writeln!(src, " pbrs::rt::encode_len_header(out, {n}, {var}.compute_size()); {var}.write_to(out);");
}
FieldType::Float => {
let _ = writeln!(src, " pbrs::rt::encode_tag(out, {n}, pbrs::rt::WIRE_I32); out.extend_from_slice(&{var}.to_bits().to_le_bytes());");
}
FieldType::Double => {
let _ = writeln!(src, " pbrs::rt::encode_tag(out, {n}, pbrs::rt::WIRE_I64); out.extend_from_slice(&{var}.to_bits().to_le_bytes());");
}
FieldType::Fixed32 => {
let _ = writeln!(src, " pbrs::rt::encode_tag(out, {n}, pbrs::rt::WIRE_I32); out.extend_from_slice(&{var}.to_le_bytes());");
}
FieldType::Sfixed32 => {
let _ = writeln!(src, " pbrs::rt::encode_tag(out, {n}, pbrs::rt::WIRE_I32); out.extend_from_slice(&(*{var} as u32).to_le_bytes());");
}
FieldType::Fixed64 => {
let _ = writeln!(src, " pbrs::rt::encode_tag(out, {n}, pbrs::rt::WIRE_I64); out.extend_from_slice(&{var}.to_le_bytes());");
}
FieldType::Sfixed64 => {
let _ = writeln!(src, " pbrs::rt::encode_tag(out, {n}, pbrs::rt::WIRE_I64); out.extend_from_slice(&(*{var} as u64).to_le_bytes());");
}
FieldType::Sint32 => {
let _ = writeln!(src, " pbrs::rt::encode_tag(out, {n}, pbrs::rt::WIRE_VARINT); pbrs::rt::encode_varint(out, pbrs::rt::encode_zigzag32(*{var}));");
}
FieldType::Sint64 => {
let _ = writeln!(src, " pbrs::rt::encode_tag(out, {n}, pbrs::rt::WIRE_VARINT); pbrs::rt::encode_varint(out, pbrs::rt::encode_zigzag64(*{var}));");
}
FieldType::Bool => {
let _ = writeln!(src, " pbrs::rt::encode_tag(out, {n}, pbrs::rt::WIRE_VARINT); pbrs::rt::encode_varint(out, u64::from(*{var}));");
}
FieldType::Uint64 => {
let _ = writeln!(src, " pbrs::rt::encode_tag(out, {n}, pbrs::rt::WIRE_VARINT); pbrs::rt::encode_varint(out, *{var});");
}
_ => {
let _ = writeln!(src, " pbrs::rt::encode_tag(out, {n}, pbrs::rt::WIRE_VARINT); pbrs::rt::encode_varint(out, *{var} as u64);");
}
}
}
fn emit_map_decoders(src: &mut String, desc: &MessageDescriptor) {
bind_field_idents(desc);
let msg = rust_ident(&desc.full_name);
for f in desc.fields.values() {
if !f.is_map {
continue;
}
let id = field_id(f).replace("r#", "");
let num = f.number;
let (k, v) = map_kv(f);
let kty = map_key_ty(f);
let vty = map_val_ty(f);
let _ = writeln!(
src,
"fn decode_map_entry_{msg}_{id}_{num}(wire: &pbrs::rt::Wire, depth: u32) -> Result<({k}, {v}), ParseError> {{"
);
let _ = writeln!(src, " let _ = depth; let data = wire.as_slice(); let mut key = {k}::default(); let mut val = {v}::default(); let mut pos = 0;");
let _ = writeln!(src, " while pos < data.len() {{ let (n, w) = pbrs::rt::decode_tag(data, &mut pos)?; match (n, w) {{");
emit_map_scalar_decode(src, 1, "key", kty, f.utf8_validate);
emit_map_scalar_decode(src, 2, "val", vty, f.utf8_validate);
let _ = writeln!(
src,
" _ => pbrs::rt::skip_field(data, &mut pos, w)?,"
);
let _ = writeln!(src, " }} }}");
let _ = writeln!(src, " Ok((key, val))");
let _ = writeln!(src, "}}");
}
}
fn emit_map_scalar_decode(src: &mut String, n: u32, var: &str, ty: FieldType, utf8: bool) {
match ty {
FieldType::String => {
if utf8 {
let _ = writeln!(src, " ({n}, pbrs::rt::WIRE_LEN) => {{ let (s, e) = pbrs::rt::read_len_span(data, &mut pos)?; pbrs::rt::require_utf8(&data[s..e])?; {var} = pbrs::rt::LazyStr::from_span(wire, s, e); }},");
} else {
let _ = writeln!(src, " ({n}, pbrs::rt::WIRE_LEN) => {{ let (s, e) = pbrs::rt::read_len_span(data, &mut pos)?; {var} = pbrs::rt::LazyStr::from_span(wire, s, e); }},");
}
}
FieldType::Bytes => {
let _ = writeln!(src, " ({n}, pbrs::rt::WIRE_LEN) => {{ let (s, e) = pbrs::rt::read_len_span(data, &mut pos)?; {var} = pbrs::rt::LazyBytes::from_wire(wire.window(s, e)); }},");
}
FieldType::Message | FieldType::Group => {
let _ = writeln!(src, " ({n}, pbrs::rt::WIRE_LEN) => {{ let (s, e) = pbrs::rt::read_len_span(data, &mut pos)?; let mut ip = 0; let mut sw = None; {var}.merge_inner(&data[s..e], &mut sw, &mut ip, depth, true, None)?; }},");
}
FieldType::Float => {
let _ = writeln!(src, " ({n}, pbrs::rt::WIRE_I32) => {var} = f32::from_bits(pbrs::rt::read_fixed32(data, &mut pos)?),");
}
FieldType::Double => {
let _ = writeln!(src, " ({n}, pbrs::rt::WIRE_I64) => {var} = f64::from_bits(pbrs::rt::read_fixed64(data, &mut pos)?),");
}
FieldType::Fixed32 => {
let _ = writeln!(src, " ({n}, pbrs::rt::WIRE_I32) => {var} = pbrs::rt::read_fixed32(data, &mut pos)?,");
}
FieldType::Sfixed32 => {
let _ = writeln!(src, " ({n}, pbrs::rt::WIRE_I32) => {var} = pbrs::rt::read_fixed32(data, &mut pos)? as i32,");
}
FieldType::Fixed64 => {
let _ = writeln!(src, " ({n}, pbrs::rt::WIRE_I64) => {var} = pbrs::rt::read_fixed64(data, &mut pos)?,");
}
FieldType::Sfixed64 => {
let _ = writeln!(src, " ({n}, pbrs::rt::WIRE_I64) => {var} = pbrs::rt::read_fixed64(data, &mut pos)? as i64,");
}
FieldType::Sint32 => {
let _ = writeln!(src, " ({n}, pbrs::rt::WIRE_VARINT) => {var} = pbrs::rt::decode_zigzag32(pbrs::rt::decode_varint(data, &mut pos)?),");
}
FieldType::Sint64 => {
let _ = writeln!(src, " ({n}, pbrs::rt::WIRE_VARINT) => {var} = pbrs::rt::decode_zigzag64(pbrs::rt::decode_varint(data, &mut pos)?),");
}
FieldType::Bool => {
let _ = writeln!(src, " ({n}, pbrs::rt::WIRE_VARINT) => {var} = pbrs::rt::decode_varint(data, &mut pos)? != 0,");
}
FieldType::Uint64 => {
let _ = writeln!(src, " ({n}, pbrs::rt::WIRE_VARINT) => {var} = pbrs::rt::decode_varint(data, &mut pos)?,");
}
FieldType::Int64 => {
let _ = writeln!(src, " ({n}, pbrs::rt::WIRE_VARINT) => {var} = pbrs::rt::decode_varint(data, &mut pos)? as i64,");
}
FieldType::Uint32 => {
let _ = writeln!(src, " ({n}, pbrs::rt::WIRE_VARINT) => {var} = pbrs::rt::decode_varint(data, &mut pos)? as u32,");
}
_ => {
let _ = writeln!(src, " ({n}, pbrs::rt::WIRE_VARINT) => {var} = pbrs::rt::decode_varint(data, &mut pos)? as i32,");
}
}
}
fn enum_api_ty(f: &FieldDescriptor) -> Option<String> {
if f.field_type != FieldType::Enum {
return None;
}
let name = f
.enum_ty
.as_ref()
.map(|e| e.full_name.as_str())
.or(f.type_name.as_deref())
.unwrap_or("UnknownEnum");
Some(rust_ident(name))
}
fn enum_first_number(f: &FieldDescriptor) -> i32 {
f.enum_ty
.as_ref()
.and_then(|e| e.listed.first().map(|x| x.0))
.unwrap_or(0)
}
fn scalar_default_expr(f: &FieldDescriptor) -> String {
if f.field_type == FieldType::Enum {
if let Some(d) = f.default.as_deref() {
if let Ok(n) = d.parse::<i32>() {
return n.to_string();
}
if let Some(n) = f.enum_ty.as_ref().and_then(|e| e.names.get(d)) {
return n.to_string();
}
}
return enum_first_number(f).to_string();
}
let Some(d) = f.default.as_deref() else {
return match f.field_type {
FieldType::Bool => "false".into(),
FieldType::Float | FieldType::Double => "0.0".into(),
_ => "0".into(),
};
};
match f.field_type {
FieldType::Bool => {
if d.eq_ignore_ascii_case("true") {
"true".into()
} else {
"false".into()
}
}
FieldType::Float | FieldType::Double => {
let f32ty = f.field_type == FieldType::Float;
let inf = if f32ty {
"f32::INFINITY"
} else {
"f64::INFINITY"
};
let ninf = if f32ty {
"f32::NEG_INFINITY"
} else {
"f64::NEG_INFINITY"
};
let nan = if f32ty { "f32::NAN" } else { "f64::NAN" };
let lower = d.to_ascii_lowercase();
if lower == "inf" || lower == "infinity" || lower == "+inf" || lower == "+infinity" {
inf.into()
} else if lower == "-inf" || lower == "-infinity" {
ninf.into()
} else if lower == "nan" {
nan.into()
} else if d.contains('.') || d.contains('e') || d.contains('E') {
d.to_string()
} else {
format!("{d}.0")
}
}
_ => d.to_string(),
}
}
fn rust_byte_lit(bytes: &[u8]) -> String {
let mut out = String::from("b\"");
for &b in bytes {
match b {
b'\\' => out.push_str("\\\\"),
b'"' => out.push_str("\\\""),
b'\n' => out.push_str("\\n"),
b'\r' => out.push_str("\\r"),
b'\t' => out.push_str("\\t"),
0x20..=0x7e => out.push(b as char),
_ => {
let _ = write!(out, "\\x{b:02x}");
}
}
}
out.push('"');
out
}
fn unescape_c_bytes(s: &str) -> Vec<u8> {
let mut out = Vec::new();
let b = s.as_bytes();
let mut i = 0;
while i < b.len() {
if b[i] == b'\\' && i + 1 < b.len() {
i += 1;
match b[i] {
b'n' => out.push(b'\n'),
b'r' => out.push(b'\r'),
b't' => out.push(b'\t'),
b'\\' => out.push(b'\\'),
b'"' => out.push(b'"'),
b'\'' => out.push(b'\''),
b'x' if i + 2 < b.len() => {
let h = std::str::from_utf8(&b[i + 1..i + 3]).unwrap_or("00");
out.push(u8::from_str_radix(h, 16).unwrap_or(0));
i += 2;
}
c if c.is_ascii_digit() => {
let mut v = 0u8;
let mut n = 0;
while i < b.len() && n < 3 && b[i].is_ascii_digit() {
v = v.saturating_mul(8).saturating_add(b[i] - b'0');
i += 1;
n += 1;
}
out.push(v);
continue;
}
c => out.push(c),
}
i += 1;
} else {
out.push(b[i]);
i += 1;
}
}
out
}
fn string_default_lit(f: &FieldDescriptor) -> String {
match f.default.as_deref() {
Some(d) => rust_byte_lit(&unescape_c_bytes(d)),
None => "b\"\"".into(),
}
}
fn bytes_default_lit(f: &FieldDescriptor) -> String {
string_default_lit(f)
}
fn camel_to_snake_name(s: &str) -> String {
to_snake(s)
}
fn screaming_snake_to_upper_camel(s: &str) -> String {
let mut out = String::new();
let mut cap = true;
for c in s.chars() {
if c.is_ascii_alphabetic() {
if cap {
out.extend(c.to_uppercase());
} else {
out.extend(c.to_lowercase());
}
cap = false;
} else if c.is_ascii_digit() {
out.push(c);
cap = true;
} else {
cap = true;
}
}
out
}
fn starts_with_ignore_ascii(name: &str, prefix: &str) -> bool {
name.len() >= prefix.len() && name[..prefix.len()].eq_ignore_ascii_case(prefix)
}
fn strip_enum_prefix<'a>(enum_name: &str, value_name: &'a str) -> &'a str {
let prefixes = [
enum_name.to_string(),
screaming_snake_to_upper_camel(enum_name),
camel_to_snake_name(enum_name),
];
for p in &prefixes {
if starts_with_ignore_ascii(value_name, p) {
let mut rest = &value_name[p.len()..];
rest = rest.strip_prefix('_').unwrap_or(rest);
if !rest.is_empty() {
return rest;
}
break;
}
}
value_name
}
fn enum_value_rs_name(enum_name: &str, value_name: &str) -> String {
let stripped = strip_enum_prefix(enum_name, value_name);
let mut name = screaming_snake_to_upper_camel(stripped);
if name.is_empty() {
name = screaming_snake_to_upper_camel(value_name);
}
if name.chars().next().is_some_and(|c| c.is_ascii_digit()) {
name = format!("_{name}");
}
if is_rust_keyword(&name) {
format!("r#{name}")
} else {
name
}
}
struct RustEnumValue {
name: String,
number: i32,
aliases: Vec<String>,
}
fn rust_enum_values(enum_name: &str, listed: &[(i32, String)]) -> Vec<RustEnumValue> {
let mut seen_name = std::collections::BTreeSet::new();
let mut by_number: std::collections::BTreeMap<i32, usize> = std::collections::BTreeMap::new();
let mut out: Vec<RustEnumValue> = Vec::new();
for (number, proto_name) in listed {
let rust_name = enum_value_rs_name(enum_name, proto_name);
if !seen_name.insert(rust_name.clone()) {
continue;
}
if let Some(&idx) = by_number.get(number) {
out[idx].aliases.push(rust_name);
} else {
by_number.insert(*number, out.len());
out.push(RustEnumValue {
name: rust_name,
number: *number,
aliases: Vec::new(),
});
}
}
out
}
fn emit_enum(src: &mut String, ed: &crate::dynamic::EnumDescriptor) {
let name = rust_ident(&ed.full_name);
let values = rust_enum_values(&ed.name, &ed.listed);
let first = ed.listed.first().map(|x| x.0).unwrap_or(0);
let known = values
.iter()
.map(|v| v.number.to_string())
.collect::<Vec<_>>()
.join(" | ");
let _ = writeln!(src, "#[repr(transparent)]");
let _ = writeln!(
src,
"#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]"
);
let _ = writeln!(src, "pub struct {name}(i32);");
let _ = writeln!(
src,
" #[allow(non_upper_case_globals, reason = \"protobuf enum names\")]"
);
let _ = writeln!(src, "impl {name} {{");
for v in &values {
let _ = writeln!(
src,
" pub const {}: {name} = {name}({});",
v.name, v.number
);
for a in &v.aliases {
let _ = writeln!(src, " pub const {a}: {name} = {name}({});", v.number);
}
}
let _ = writeln!(src, " fn constant_name(self) -> Option<&'static str> {{");
let _ = writeln!(
src,
" #[allow(unreachable_patterns, reason = \"open enums\")]"
);
let _ = writeln!(src, " Some(match self.0 {{");
for v in &values {
let _ = writeln!(src, " {} => \"{}\",", v.number, v.name);
}
let _ = writeln!(src, " _ => return None,");
let _ = writeln!(src, " }})");
let _ = writeln!(src, " }}");
let _ = writeln!(src, "}}");
let _ = writeln!(src, "impl From<{name}> for i32 {{");
let _ = writeln!(src, " fn from(v: {name}) -> i32 {{ v.0 }}");
let _ = writeln!(src, "}}");
if ed.closed {
let _ = writeln!(src, "impl TryFrom<i32> for {name} {{");
let _ = writeln!(src, " type Error = UnknownEnumValue<Self>;");
let _ = writeln!(
src,
" fn try_from(val: i32) -> Result<Self, Self::Error> {{"
);
let _ = writeln!(
src,
" if <Self as Enum>::is_known(val) {{ Ok(Self(val)) }} else {{ Err(UnknownEnumValue::new(pbrs::__internal::Private, val)) }}"
);
let _ = writeln!(src, " }}");
let _ = writeln!(src, "}}");
} else {
let _ = writeln!(src, "impl From<i32> for {name} {{");
let _ = writeln!(src, " fn from(val: i32) -> Self {{ Self(val) }}");
let _ = writeln!(src, "}}");
}
let _ = writeln!(src, "impl Default for {name} {{");
let _ = writeln!(src, " fn default() -> Self {{ Self({first}) }}");
let _ = writeln!(src, "}}");
let _ = writeln!(src, "impl std::fmt::Debug for {name} {{");
let _ = writeln!(
src,
" fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {{"
);
let _ = writeln!(
src,
" if let Some(n) = self.constant_name() {{ write!(f, \"{name}::{{n}}\") }} else {{ write!(f, \"{name}::from({{}})\", self.0) }}"
);
let _ = writeln!(src, " }}");
let _ = writeln!(src, "}}");
let _ = writeln!(src, "impl pbrs::__internal::SealedInternal for {name} {{}}");
let _ = writeln!(
src,
"impl pbrs::Proxied for {name} {{ type View<'msg> = {name}; }}"
);
let _ = writeln!(src, "impl pbrs::AsView for {name} {{");
let _ = writeln!(src, " type Proxied = Self;");
let _ = writeln!(src, " fn as_view(&self) -> Self {{ *self }}");
let _ = writeln!(src, "}}");
let _ = writeln!(src, "impl<'msg> pbrs::IntoView<'msg> for {name} {{");
let _ = writeln!(
src,
" fn into_view<'shorter>(self) -> Self where 'msg: 'shorter {{ self }}"
);
let _ = writeln!(src, "}}");
let _ = writeln!(src, "impl Enum for {name} {{");
let _ = writeln!(src, " const NAME: &'static str = \"{name}\";");
if known.is_empty() {
let _ = writeln!(src, " fn is_known(_: i32) -> bool {{ false }}");
} else {
let _ = writeln!(
src,
" fn is_known(value: i32) -> bool {{ matches!(value, {known}) }}"
);
}
let _ = writeln!(src, "}}");
}
fn emit_nested_mods(src: &mut String, messages: &[String], enums: &[String]) {
let msg_set: std::collections::BTreeSet<&str> = messages.iter().map(|s| s.as_str()).collect();
#[derive(Default)]
struct Node {
children: std::collections::BTreeMap<String, Node>,
items: Vec<(String, String)>,
}
let mut root = Node::default();
let mut add = |full: &str| {
let ident = rust_ident(full);
let last = ident_last(full);
let mut cur = full;
let mut path = Vec::new();
while let Some((parent, _)) = cur.rsplit_once('.') {
if !msg_set.contains(parent) {
break;
}
path.push(to_snake(&ident_last(parent)));
cur = parent;
}
if path.is_empty() {
return;
}
path.reverse();
let mut node = &mut root;
for p in &path {
node = node.children.entry(p.clone()).or_default();
}
node.items.push((last, ident));
};
for m in messages {
add(m);
}
for e in enums {
add(e);
}
fn emit_node(src: &mut String, name: &str, node: &Node, depth: usize) {
let _ = writeln!(src, "pub mod {name} {{");
let prefix = "super::".repeat(depth);
for (export, ident) in &node.items {
if export == ident {
let _ = writeln!(src, " pub use {prefix}{ident};");
} else {
let _ = writeln!(src, " pub use {prefix}{ident} as {export};");
}
}
for (child_name, child) in &node.children {
emit_node(src, child_name, child, depth + 1);
}
let _ = writeln!(src, "}}");
}
for (name, node) in &root.children {
emit_node(src, name, node, 1);
}
}
fn to_snake(s: &str) -> String {
let mut out = String::new();
for (i, c) in s.chars().enumerate() {
if c.is_uppercase() {
if i > 0 {
out.push('_');
}
out.extend(c.to_lowercase());
} else {
out.push(c);
}
}
if is_rust_keyword(&out) {
format!("r#{out}")
} else {
out
}
}
/// Fully qualified path to the kernel crate. Generated code never relies on a
/// `use` the surrounding module might not have.
const G: &str = "::pbrs_grpc";
/// The four gRPC call shapes, as they appear in generated signatures.
struct Shape {
/// Request envelope contents in the service trait.
trait_request: String,
/// Response envelope contents in the service trait.
trait_response: String,
/// [`Rpc`] method the server dispatches through.
dispatch: &'static str,
/// Client method signature and body.
client_arg: String,
client_return: String,
client_call: &'static str,
/// Human description for the emitted doc comment.
description: &'static str,
}
fn shape_of(m: &MethodDescriptor) -> Shape {
let req = rust_ident(&m.input_type);
let resp = rust_ident(&m.output_type);
match (m.client_streaming, m.server_streaming) {
(false, false) => Shape {
trait_request: req.clone(),
trait_response: resp.clone(),
dispatch: "unary",
client_arg: format!("{G}::Request<{req}>"),
client_return: format!("{G}::Call<{G}::Response<{resp}>>"),
client_call: "unary",
description: "Unary",
},
(true, false) => Shape {
trait_request: format!("{G}::Streaming<{req}>"),
trait_response: resp.clone(),
dispatch: "client_streaming",
client_arg: format!("{G}::Request<()>"),
client_return: format!("({G}::StreamSender<{req}>, {G}::Call<{G}::Response<{resp}>>)"),
client_call: "client_streaming",
description: "Client-streaming",
},
(false, true) => Shape {
trait_request: req.clone(),
trait_response: format!("{G}::Streaming<{resp}>"),
dispatch: "server_streaming",
client_arg: format!("{G}::Request<{req}>"),
client_return: format!("{G}::Call<{G}::Response<{G}::Streaming<{resp}>>>"),
client_call: "server_streaming",
description: "Server-streaming",
},
(true, true) => Shape {
trait_request: format!("{G}::Streaming<{req}>"),
trait_response: format!("{G}::Streaming<{resp}>"),
dispatch: "bidi_streaming",
client_arg: format!("{G}::Request<()>"),
client_return: format!(
"({G}::StreamSender<{req}>, {G}::Call<{G}::Response<{G}::Streaming<{resp}>>>)"
),
client_call: "bidi",
description: "Bidirectional-streaming",
},
}
}
/// Emit `pbrs-grpc` stubs for one service: the handler trait, a server that
/// implements [`Service`](../../pbrs_grpc/trait.Service.html), and a client.
fn emit_kernel_service(src: &mut String, svc: &ServiceDescriptor) {
let svc_ty = rust_ident(&svc.full_name);
let trait_name = svc_ty.clone();
let server = format!("{svc_ty}Server");
let client = format!("{svc_ty}Client");
let full_name = &svc.full_name;
emit_kernel_trait(src, &trait_name, svc);
emit_kernel_server(src, &trait_name, &server, full_name, svc);
emit_kernel_client(src, &trait_name, &client, full_name, svc);
}
fn emit_kernel_trait(src: &mut String, trait_name: &str, svc: &ServiceDescriptor) {
let _ = writeln!(src, "/// The `{}` service.", svc.full_name);
let _ = writeln!(src, "///");
let _ = writeln!(
src,
"/// Implement this, then serve it with [`{trait_name}Server`]."
);
let _ = writeln!(src, "///");
let _ = writeln!(
src,
"/// Methods you omit return [`{G}::Status::unimplemented`]."
);
let _ = writeln!(src, "pub trait {trait_name}: Send + Sync + 'static {{");
for m in &svc.methods {
let shape = shape_of(m);
let fn_name = to_snake(&m.name);
let _ = writeln!(src, " /// {} `{}`.", shape.description, m.name);
let _ = writeln!(src, " ///");
let _ = writeln!(
src,
" /// The default answers [`{G}::Status::unimplemented`]."
);
let _ = writeln!(
src,
" /// Inbound `grpc-encoding` is [`{G}::Request::encoding`] (`None` for identity)."
);
let _ = writeln!(
src,
" /// Spawned work should await [`{G}::Request::cancelled`]; the kernel drops a still-pending handler on client RST."
);
let _ = writeln!(
src,
" /// [`{G}::Status::from_error_details`] is the typed bag after a generated handler Err; those trailers reach the client."
);
let _ = writeln!(
src,
" /// Distinct from a generated server intercept Err: that is trailers without reading the body; this generated handler Err is after the handler ran."
);
let _ = writeln!(
src,
" /// Distinct from a generated intercept Err: that is a local reject never opens a stream; this generated handler Err is after the handler ran."
);
let _ = writeln!(
src,
" /// Distinct from a generated server on_response Err: that is trailers-only after handler Ok; this generated handler Err is after the handler ran."
);
let _ = writeln!(
src,
" /// Distinct from a generated client on_response Err: that fails the Call after a successful receive; this generated handler Err is after the handler ran."
);
let _ = writeln!(
src,
" /// Distinct from a generated StreamSender fail: that is trailers after any messages already sent; this generated handler Err is after the handler ran."
);
if m.server_streaming {
let _ = writeln!(
src,
" /// Spawned producers should select on [`{G}::Request::cancelled`] and [`{G}::StreamSender::closed`]; drain aborts on client RST."
);
let _ = writeln!(
src,
" /// [`{G}::StreamSender::fail`] after a message ships trailing metadata and `grpc-status-details-bin` the same as a handler `Err`."
);
let _ = writeln!(
src,
" /// [`{G}::Status::from_error_details`] is the typed bag after a generated StreamSender fail on a server response producer; those trailers ship after any messages already sent."
);
let _ = writeln!(
src,
" /// Distinct from a generated handler Err: that is after the handler ran; this generated StreamSender fail is trailers after any messages already sent."
);
let _ = writeln!(
src,
" /// Distinct from a generated server intercept Err: that is trailers without reading the body; this generated StreamSender fail is trailers after any messages already sent."
);
let _ = writeln!(
src,
" /// Distinct from a generated server on_response Err: that is trailers-only after handler Ok; this generated StreamSender fail is trailers after any messages already sent."
);
let _ = writeln!(
src,
" /// Distinct from a generated intercept Err: that is a local reject never opens a stream; this generated StreamSender fail is trailers after any messages already sent."
);
let _ = writeln!(
src,
" /// Distinct from a generated client on_response Err: that fails the Call after a successful receive; this generated StreamSender fail is trailers after any messages already sent."
);
}
if m.client_streaming && !m.server_streaming {
let _ = writeln!(
src,
" /// That signal still fires after the client half-closes the request stream."
);
}
let _ = writeln!(src, " fn {fn_name}(");
let _ = writeln!(src, " &self,");
let _ = writeln!(
src,
" request: {G}::Request<{}>,",
shape.trait_request
);
let _ = writeln!(
src,
" ) -> impl ::core::future::Future<Output = ::core::result::Result<{G}::Response<{}>, {G}::Status>> + Send {{",
shape.trait_response
);
let _ = writeln!(src, " async move {{");
let _ = writeln!(src, " drop(request);");
let _ = writeln!(
src,
" ::core::result::Result::Err({G}::Status::unimplemented(\"method {}/{} not implemented\"))",
svc.full_name,
m.name
);
let _ = writeln!(src, " }}");
let _ = writeln!(src, " }}");
}
let _ = writeln!(src, "}}");
}
fn emit_kernel_server(
src: &mut String,
trait_name: &str,
server: &str,
full_name: &str,
svc: &ServiceDescriptor,
) {
let _ = writeln!(src, "/// Serves an implementation of [`{trait_name}`].");
let _ = writeln!(src, "///");
let _ = writeln!(
src,
"/// [`Self::rpc_timeout`], [`Self::compresses_outbound`], [`Self::gzip_level`], [`Self::accepts_compressed`], [`Self::concurrent_rpc_limit`], [`Self::send_buffer_size`], and [`Self::limits`] read the server overlay without colliding with the setters. Same getters as [`{G}::Server`] / [`{G}::Router`]."
);
let _ = writeln!(src, "pub struct {server}<T> {{");
let _ = writeln!(src, " inner: ::std::sync::Arc<T>,");
let _ = writeln!(src, " config: {G}::ServerConfig,");
let _ = writeln!(src, "}}");
let _ = writeln!(src, "impl<T> ::core::clone::Clone for {server}<T> {{");
let _ = writeln!(src, " fn clone(&self) -> Self {{");
let _ = writeln!(src, " Self {{");
let _ = writeln!(
src,
" inner: ::std::sync::Arc::clone(&self.inner),"
);
let _ = writeln!(src, " config: self.config,");
let _ = writeln!(src, " }}");
let _ = writeln!(src, " }}");
let _ = writeln!(src, "}}");
let _ = writeln!(src, "impl<T> {server}<T> {{");
let _ = writeln!(
src,
" /// Wrap an existing `Arc` without adding another layer."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn from_arc(inner: ::std::sync::Arc<T>) -> Self {{"
);
let _ = writeln!(src, " Self {{");
let _ = writeln!(src, " inner,");
let _ = writeln!(
src,
" config: <{G}::ServerConfig as ::core::default::Default>::default(),"
);
let _ = writeln!(src, " }}");
let _ = writeln!(src, " }}");
let _ = writeln!(src, " /// Take the inner `Arc` back.");
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn into_inner(self) -> ::std::sync::Arc<T> {{ self.inner }}"
);
let _ = writeln!(src, "}}");
let _ = writeln!(src, "impl<T> ::core::fmt::Debug for {server}<T> {{");
let _ = writeln!(
src,
" fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {{"
);
let _ = writeln!(
src,
" f.debug_struct(\"{server}\").field(\"service\", &\"{full_name}\").field(\"config\", &self.config).finish()"
);
let _ = writeln!(src, " }}");
let _ = writeln!(src, "}}");
let _ = writeln!(src, "impl<T: {trait_name}> {server}<T> {{");
let _ = writeln!(src, " /// Fully qualified proto service name.");
let _ = writeln!(src, " pub const NAME: &'static str = \"{full_name}\";");
let _ = writeln!(
src,
" /// Wrap an implementation with default configuration."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(src, " pub fn new(inner: T) -> Self {{");
let _ = writeln!(src, " Self {{");
let _ = writeln!(src, " inner: ::std::sync::Arc::new(inner),");
let _ = writeln!(
src,
" config: <{G}::ServerConfig as ::core::default::Default>::default(),"
);
let _ = writeln!(src, " }}");
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" /// Replace the transport and limit configuration."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn config(mut self, config: {G}::ServerConfig) -> Self {{ self.config = config; self }}"
);
let _ = writeln!(
src,
" /// The configuration in effect. Distinct from [`Self::config`], which replaces it. See [`{G}::Server::server_config`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn server_config(&self) -> {G}::ServerConfig {{ self.config }}"
);
let _ = writeln!(
src,
" /// Cap inbound messages at `limit` bytes. Default 4 MiB. Applies to every call shape, including over TLS, mTLS, Unix, and [`{G}::Server::serve_connection`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn max_decoding_message_size(mut self, limit: usize) -> Self {{ self.config = self.config.max_decoding_message_size(limit); self }}"
);
let _ = writeln!(
src,
" /// Cap outbound messages at `limit` bytes. Default unlimited. Applies to every call shape."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn max_encoding_message_size(mut self, limit: usize) -> Self {{ self.config = self.config.max_encoding_message_size(limit); self }}"
);
let _ = writeln!(
src,
" /// Replace both message caps at once. Applies to every call shape. See [`{G}::ServerConfig::message_limits`]."
);
let _ = writeln!(
src,
" /// Distinct from [`Self::max_decoding_message_size`] / [`Self::max_encoding_message_size`]. Oversize inbound or outbound is `RESOURCE_EXHAUSTED` on every call shape, including over TLS, mTLS, Unix, and [`{G}::Server::serve_connection`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn message_limits(mut self, limits: {G}::MessageLimits) -> Self {{ self.config = self.config.message_limits(limits); self }}"
);
let _ = writeln!(
src,
" /// Configured message caps. Distinct from [`Self::message_limits`], which sets them. Distinct from [`Self::send_buffer_size`]: that is the HTTP/2 send buffer, not uncompressed protobuf bytes. See [`{G}::Server::limits`]. Applies to every call shape."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn limits(&self) -> {G}::MessageLimits {{ self.config.limits() }}"
);
let _ = writeln!(
src,
" /// Cap how many RPCs the process will run at once. Applies to every call shape, including over TLS, mTLS, Unix, and [`{G}::Server::serve_connection`]. See [`{G}::ServerConfig::max_concurrent_rpcs`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn max_concurrent_rpcs(mut self, n: usize) -> Self {{ self.config = self.config.max_concurrent_rpcs(n); self }}"
);
let _ = writeln!(
src,
" /// Configured process-wide RPC cap, if any. Distinct from [`Self::max_concurrent_rpcs`], which sets it. See [`{G}::Server::concurrent_rpc_limit`]. Applies to every call shape."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn concurrent_rpc_limit(&self) -> ::core::option::Option<usize> {{ self.config.concurrent_rpc_limit() }}"
);
let _ = writeln!(
src,
" /// Cap how many TCP/Unix connections the accept loop will serve at once, including TLS and mTLS listeners. Applies to every call shape. See [`{G}::ServerConfig::max_concurrent_connections`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn max_concurrent_connections(mut self, n: usize) -> Self {{ self.config = self.config.max_concurrent_connections(n); self }}"
);
let _ = writeln!(
src,
" /// Concurrent RPCs allowed per HTTP/2 connection. Applies to every call shape. See [`{G}::ServerConfig::max_concurrent_streams`]."
);
let _ = writeln!(
src,
" /// HTTP/2 `SETTINGS_MAX_CONCURRENT_STREAMS`. Distinct from [`Self::max_concurrent_rpcs`], which refuses extras as `RESOURCE_EXHAUSTED`. A well-behaved client waits; both RPCs still complete, including over TLS, mTLS, Unix, and [`{G}::Server::serve_connection`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn max_concurrent_streams(mut self, streams: u32) -> Self {{ self.config = self.config.max_concurrent_streams(streams); self }}"
);
let _ = writeln!(
src,
" /// HTTP/2 per-stream receive window. Applies to every call shape. See [`{G}::ServerConfig::initial_stream_window_size`]."
);
let _ = writeln!(
src,
" /// A well-behaved client still completes every call shape, including over TLS, mTLS, Unix, and [`{G}::Server::serve_connection`]. Distinct from [`Self::max_frame_size`], which still serves at the 16 KiB SETTINGS minimum, and from [`Self::max_concurrent_streams`], which serializes extra RPCs."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn initial_stream_window_size(mut self, bytes: u32) -> Self {{ self.config = self.config.initial_stream_window_size(bytes); self }}"
);
let _ = writeln!(
src,
" /// HTTP/2 per-connection receive window. Applies to every call shape. See [`{G}::ServerConfig::initial_connection_window_size`]."
);
let _ = writeln!(
src,
" /// A well-behaved client still completes every call shape, including over TLS, mTLS, Unix, and [`{G}::Server::serve_connection`]. Distinct from [`Self::max_frame_size`], which still serves at the 16 KiB SETTINGS minimum, and from [`Self::max_concurrent_streams`], which serializes extra RPCs."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn initial_connection_window_size(mut self, bytes: u32) -> Self {{ self.config = self.config.initial_connection_window_size(bytes); self }}"
);
let _ = writeln!(
src,
" /// HTTP/2 `SETTINGS_MAX_FRAME_SIZE`. Applies to every call shape. See [`{G}::ServerConfig::max_frame_size`]."
);
let _ = writeln!(
src,
" /// A well-behaved client splits DATA; every call shape still completes, including over TLS, mTLS, Unix, and [`{G}::Server::serve_connection`]. Distinct from [`Self::max_header_list_size`], which refuses oversize metadata, and from [`Self::max_concurrent_streams`], which serializes extra RPCs."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn max_frame_size(mut self, bytes: u32) -> Self {{ self.config = self.config.max_frame_size(bytes); self }}"
);
let _ = writeln!(
src,
" /// HTTP/2 `SETTINGS_MAX_HEADER_LIST_SIZE`. Applies to every call shape. See [`{G}::ServerConfig::max_header_list_size`]."
);
let _ = writeln!(
src,
" /// Oversize metadata is refused, including over TLS, mTLS, Unix, and [`{G}::Server::serve_connection`]. Distinct from a raw HTTP/2 peer."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn max_header_list_size(mut self, bytes: u32) -> Self {{ self.config = self.config.max_header_list_size(bytes); self }}"
);
let _ = writeln!(
src,
" /// HTTP/2 `SETTINGS_HEADER_TABLE_SIZE` (HPACK dynamic table). Default 4096. Applies to every call shape. Distinct from [`Self::max_header_list_size`], which caps uncompressed header-block bytes (`SETTINGS_MAX_HEADER_LIST_SIZE`). A well-behaved client still completes every call shape at this table size, including over TLS, mTLS, Unix, and [`{G}::Server::serve_connection`]. See [`{G}::ServerConfig::header_table_size`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn header_table_size(mut self, bytes: u32) -> Self {{ self.config = self.config.header_table_size(bytes); self }}"
);
let _ = writeln!(
src,
" /// HTTP/2 small-DATA framing budget. Default 25600. Applies to every call shape. Caps extra memory from tiny DATA frames. Exceeding this is `ENHANCE_YOUR_CALM` (`too_many_data_frames`). Distinct from [`Self::initial_connection_window_size`], which is flow-control bytes, and from [`Self::max_frame_size`], which caps one DATA payload. h2 Auto (half the connection window) is not exposed. A well-behaved client still completes every call shape at this framing budget, including over TLS, mTLS, Unix, and [`{G}::Server::serve_connection`]. See [`{G}::ServerConfig::data_frame_budget`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn data_frame_budget(mut self, bytes: usize) -> Self {{ self.config = self.config.data_frame_budget(bytes); self }}"
);
let _ = writeln!(
src,
" /// Per-connection HTTP/2 send buffer. Applies to every call shape. See [`{G}::ServerConfig::max_send_buffer_size`]."
);
let _ = writeln!(
src,
" /// Write backpressure still completes every call shape, including over TLS, mTLS, Unix, and [`{G}::Server::serve_connection`]. Distinct from [`Self::max_frame_size`], which still serves at the 16 KiB SETTINGS minimum, and from [`Self::initial_stream_window_size`], which still serves at a small receive window."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn max_send_buffer_size(mut self, bytes: usize) -> Self {{ self.config = self.config.max_send_buffer_size(bytes); self }}"
);
let _ = writeln!(
src,
" /// Configured write-time HTTP/2 send buffer. Distinct from [`Self::max_send_buffer_size`], which sets it. Distinct from [`Self::message_limits`]: that is uncompressed protobuf bytes, not this send buffer. See [`{G}::Server::send_buffer_size`]. Applies to every call shape."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn send_buffer_size(&self) -> usize {{ self.config.send_buffer_size() }}"
);
let _ = writeln!(
src,
" /// Cap remotely-reset HTTP/2 streams waiting in the accept queue. Applies to every call shape. See [`{G}::ServerConfig::max_pending_accept_reset_streams`]."
);
let _ = writeln!(
src,
" /// A well-behaved client never fills that queue; every call shape still completes, including over TLS, mTLS, Unix, and [`{G}::Server::serve_connection`]. Distinct from a raw HTTP/2 peer."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn max_pending_accept_reset_streams(mut self, n: usize) -> Self {{ self.config = self.config.max_pending_accept_reset_streams(n); self }}"
);
let _ = writeln!(
src,
" /// Cap locally-reset HTTP/2 streams caused by a peer protocol error. Default 1024. Exceeding this is `ENHANCE_YOUR_CALM`. Distinct from [`Self::max_pending_accept_reset_streams`], which caps remotely-reset streams (rapid reset). This caps RSTs we send after an invalid frame. A well-behaved client never triggers one; every call shape still completes, including over TLS, mTLS, Unix, and [`{G}::Server::serve_connection`]. See [`{G}::ServerConfig::max_local_error_reset_streams`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn max_local_error_reset_streams(mut self, n: usize) -> Self {{ self.config = self.config.max_local_error_reset_streams(n); self }}"
);
let _ = writeln!(
src,
" /// Cap remembered locally-reset HTTP/2 stream IDs. Default 50. When the cap is reached, the oldest ID is purged from memory, not `ENHANCE_YOUR_CALM`. Frames on a purged ID are a connection `PROTOCOL_ERROR`. Distinct from [`Self::max_pending_accept_reset_streams`] (rapid-reset GOAWAY) and [`Self::max_local_error_reset_streams`] (protocol-error RST GOAWAY). A well-behaved client still completes every call shape at this memory cap, including over TLS, mTLS, Unix, and [`{G}::Server::serve_connection`]. See [`{G}::ServerConfig::max_concurrent_reset_streams`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn max_concurrent_reset_streams(mut self, n: usize) -> Self {{ self.config = self.config.max_concurrent_reset_streams(n); self }}"
);
let _ = writeln!(
src,
" /// How long locally-reset HTTP/2 stream IDs are remembered. Default 1 s. After this duration the ID is forgotten, not `ENHANCE_YOUR_CALM`. Frames on a forgotten ID are a connection `PROTOCOL_ERROR`. Distinct from [`Self::max_concurrent_reset_streams`], which is how many IDs are remembered (count). This is how long (time). A well-behaved client still completes every call shape at this reset duration, including over TLS, mTLS, Unix, and [`{G}::Server::serve_connection`]. See [`{G}::ServerConfig::reset_stream_duration`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn reset_stream_duration(mut self, dur: ::std::time::Duration) -> Self {{ self.config = self.config.reset_stream_duration(dur); self }}"
);
let _ = writeln!(
src,
" /// Cap every RPC even when the client omits `grpc-timeout`. Applies to every call shape, including over TLS, mTLS, Unix, and [`{G}::Server::serve_connection`]. See [`{G}::ServerConfig::timeout`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn timeout(mut self, timeout: ::std::time::Duration) -> Self {{ self.config = self.config.timeout(timeout); self }}"
);
let _ = writeln!(
src,
" /// gzip responses when the client advertises gzip. Applies to every call shape, including over TLS, mTLS, Unix, and [`{G}::Server::serve_connection`]. See [`{G}::ServerConfig::send_compressed`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn send_compressed(mut self) -> Self {{ self.config = self.config.send_compressed(true); self }}"
);
let _ = writeln!(
src,
" /// Deflate effort for outbound gzip. Default 1 (`flate2` fast). Applies to every call shape. Distinct from [`Self::send_compressed`], which is on or off. 0 stores; 9 is best. A well-behaved client still completes every call shape, including over TLS, mTLS, Unix, and [`{G}::Server::serve_connection`]. See [`{G}::ServerConfig::gzip_compression_level`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn gzip_compression_level(mut self, level: u32) -> Self {{ self.config = self.config.gzip_compression_level(level); self }}"
);
let _ = writeln!(
src,
" /// Inflate inbound gzip. Default `true`. Applies to every call shape, including over TLS, mTLS, Unix, and [`{G}::Server::serve_connection`]. Passing `false` refuses `grpc-encoding: gzip` as `UNIMPLEMENTED` before the handler. Distinct from [`Self::send_compressed`], which is outbound. See [`{G}::ServerConfig::accept_compressed`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn accept_compressed(mut self, accept: bool) -> Self {{ self.config = self.config.accept_compressed(accept); self }}"
);
let _ = writeln!(
src,
" /// Cap every RPC even when the client omits `grpc-timeout`. Distinct from [`Self::timeout`], which sets it. Interceptors and handlers read the same overlay on [`{G}::Rpc::rpc_timeout`] / [`{G}::Request::rpc_timeout`]. See [`{G}::Server::rpc_timeout`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn rpc_timeout(&self) -> ::core::option::Option<::std::time::Duration> {{ self.config.rpc_timeout() }}"
);
let _ = writeln!(
src,
" /// Whether responses are gzipped when the client accepts gzip. Distinct from [`Self::send_compressed`], which enables it. See [`{G}::Server::compresses_outbound`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn compresses_outbound(&self) -> bool {{ self.config.compresses_outbound() }}"
);
let _ = writeln!(
src,
" /// Configured outbound gzip deflate level. Distinct from [`Self::gzip_compression_level`], which sets it. See [`{G}::Server::gzip_level`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn gzip_level(&self) -> u32 {{ self.config.gzip_level() }}"
);
let _ = writeln!(
src,
" /// Whether inbound gzip is inflated. Default `true`. Distinct from [`Self::accept_compressed`], which sets it. Distinct from [`{G}::Rpc::accepts_gzip`], which is the peer's `grpc-accept-encoding`. See [`{G}::Server::accepts_compressed`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn accepts_compressed(&self) -> bool {{ self.config.accepts_compressed() }}"
);
let _ = writeln!(
src,
" /// HTTP/2 PING keepalive. Applies to every call shape. See [`{G}::ServerConfig::keep_alive_interval`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn keep_alive_interval(mut self, interval: ::std::time::Duration) -> Self {{ self.config = self.config.keep_alive_interval(interval); self }}"
);
let _ = writeln!(
src,
" /// How long to wait for a PING acknowledgement. Applies to every call shape. See [`{G}::ServerConfig::keep_alive_timeout`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn keep_alive_timeout(mut self, timeout: ::std::time::Duration) -> Self {{ self.config = self.config.keep_alive_timeout(timeout); self }}"
);
let _ = writeln!(
src,
" /// TCP `SO_KEEPALIVE`. Applies to every call shape. See [`{G}::ServerConfig::tcp_keepalive`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn tcp_keepalive(mut self, time: ::std::time::Duration) -> Self {{ self.config = self.config.tcp_keepalive(time); self }}"
);
let _ = writeln!(
src,
" /// TCP `TCP_KEEPINTVL` probe interval. Applies only when [`Self::tcp_keepalive`] is set; this does not turn `SO_KEEPALIVE` on by itself. Distinct from [`Self::keep_alive_interval`] (HTTP/2 PING). See [`{G}::ServerConfig::tcp_keepalive_interval`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn tcp_keepalive_interval(mut self, interval: ::std::time::Duration) -> Self {{ self.config = self.config.tcp_keepalive_interval(interval); self }}"
);
let _ = writeln!(
src,
" /// TCP `TCP_KEEPCNT` probe count. Applies only when [`Self::tcp_keepalive`] is set; this does not turn `SO_KEEPALIVE` on by itself. Distinct from [`Self::tcp_keepalive_interval`] (`TCP_KEEPINTVL` time, not count). See [`{G}::ServerConfig::tcp_keepalive_retries`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn tcp_keepalive_retries(mut self, retries: u32) -> Self {{ self.config = self.config.tcp_keepalive_retries(retries); self }}"
);
let _ = writeln!(
src,
" /// Send GOAWAY this long after accept. The next RPC of every call shape redials, including over TLS, mTLS, and Unix; transparent retry of the same in-flight RPC is unary and server-streaming after request bytes, client-streaming and bidi before HEADERS. See [`{G}::ServerConfig::max_connection_age`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn max_connection_age(mut self, age: ::std::time::Duration) -> Self {{ self.config = self.config.max_connection_age(age); self }}"
);
let _ = writeln!(
src,
" /// Send GOAWAY after this long with no outstanding RPCs. The next RPC of every call shape redials, including over TLS, mTLS, and Unix. See [`{G}::ServerConfig::max_connection_idle`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn max_connection_idle(mut self, idle: ::std::time::Duration) -> Self {{ self.config = self.config.max_connection_idle(idle); self }}"
);
let _ = writeln!(
src,
" /// After age or idle fires, wait this long for in-flight RPCs, including over TLS, mTLS, Unix, and [`{G}::Server::serve_connection`]. Applies to every call shape. See [`{G}::ServerConfig::max_connection_age_grace`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn max_connection_age_grace(mut self, grace: ::std::time::Duration) -> Self {{ self.config = self.config.max_connection_age_grace(grace); self }}"
);
let _ = writeln!(
src,
" /// Drop a client that never finishes TLS or the HTTP/2 preface. Applies to every call shape, including over TLS, mTLS, and Unix. See [`{G}::ServerConfig::handshake_timeout`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn handshake_timeout(mut self, timeout: ::std::time::Duration) -> Self {{ self.config = self.config.handshake_timeout(timeout); self }}"
);
let _ = writeln!(
src,
" /// Run `interceptor` before `{trait_name}` methods. It may mutate metadata, cap the deadline (`set_timeout` / `deadline` Instant), inspect path / service / method, `:authority` / `:scheme` / `peer_timeout` / `rpc_timeout` / `effective_timeout` / `local_addr` / `remote_addr` / `peer_identity` / `peer_cred` / message caps / gzip accept and encoding / `compresses_outbound` / `gzip_level` / `accepts_compressed` / `concurrent_rpc_limit` / `send_buffer_size`, attach extensions, or reject. Generated handlers see the same values on [`{G}::Request`]. Calling this twice stacks: the first interceptor runs first. Applies to every call shape; `Err` rejects before the body is read, including over TLS, mTLS, Unix, and [`{G}::Server::serve_connection`]. `Err` may carry [`{G}::Status::with_error_details`]; those trailers reach the client."
);
let _ = writeln!(
src,
" /// [`{G}::Status::from_error_details`] is the typed bag after a generated server intercept Err; those trailers reach the client without reading the body."
);
let _ = writeln!(
src,
" /// Distinct from a generated handler Err: that is after the handler ran; this generated server intercept Err is trailers without reading the body."
);
let _ = writeln!(
src,
" /// Distinct from a generated server on_response Err: that is trailers-only after handler Ok; this generated server intercept Err is trailers without reading the body."
);
let _ = writeln!(
src,
" /// Distinct from a generated intercept Err: that is a local reject never opens a stream; this generated server intercept Err is trailers without reading the body."
);
let _ = writeln!(
src,
" /// Distinct from a generated client on_response Err: that fails the Call after a successful receive; this generated server intercept Err is trailers without reading the body."
);
let _ = writeln!(
src,
" /// Distinct from a generated StreamSender fail: that is trailers after any messages already sent; this generated server intercept Err is trailers without reading the body."
);
let _ = writeln!(
src,
" /// Distinct from [`{G}::Channel::intercept`]: that runs on the outbound call before the stream opens; this generated server intercept runs on the inbound RPC before the handler."
);
let _ = writeln!(
src,
" /// Distinct from [`Self::on_response`]: that runs after the handler returns Ok; this generated server intercept runs on the inbound RPC before the handler."
);
let _ = writeln!(
src,
" /// Compiling overlay dumps live on [`{G}::hello`] (`GreeterServer::new(Svc).intercept`)."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn intercept<I>(self, interceptor: I) -> {G}::Server<Self>"
);
let _ = writeln!(src, " where");
let _ = writeln!(src, " I: {G}::Interceptor,");
let _ = writeln!(src, " {{");
let _ = writeln!(src, " self.into_server().intercept(interceptor)");
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" /// Run `interceptor` after `{trait_name}` methods return `Ok`. It may mutate headers, trailers, compress, and local extensions (`Response::extensions` is not on the wire; stamp metadata here to send it). Calling this twice stacks: the first interceptor runs first. Applies to every call shape, including over TLS, mTLS, Unix, and [`{G}::Server::serve_connection`]. `Err` after the handler already ran; that status is sent trailers-only instead of the response, including [`{G}::Status::with_error_details`]. A handler `Err` skips this hook. [`{G}::ResponseParts::path`] is kernel-stamped. Distinct from [`{G}::Request::path`]. [`{G}::ResponseParts::gzip_level`] is the server encode overlay. Distinct from [`{G}::ResponseParts::compress`]. [`{G}::ResponseParts::compresses_outbound`] is the server encode overlay. Distinct from [`{G}::ResponseParts::compress`], which is the per-RPC Compressed-Flag. [`{G}::ResponseParts::accepts_gzip`] is the peer `grpc-accept-encoding` advertisement. Distinct from [`{G}::ResponseParts::encoding`]. [`{G}::ResponseParts::deadline`] is kernel-stamped when writing. Distinct from [`{G}::Request::deadline`]. Distinct from [`{G}::Rpc::deadline`]. [`{G}::ResponseParts::timeout`] is the duration stamped at dispatch. Distinct from [`{G}::ResponseParts::deadline`]. [`{G}::ResponseParts::limits`] is the encode cap when writing. Distinct from [`{G}::Request::limits`]. Distinct from [`{G}::Rpc::limits`]. [`{G}::ResponseParts::peer_timeout`] is the client's `grpc-timeout`. Distinct from [`{G}::ResponseParts::timeout`]. [`{G}::ResponseParts::rpc_timeout`] is the server overlay. Distinct from [`{G}::ResponseParts::timeout`]. Distinct from [`{G}::ResponseParts::peer_timeout`]. [`{G}::ResponseParts::accepts_compressed`] is the inbound gzip overlay. Distinct from [`{G}::ResponseParts::accepts_gzip`]. [`{G}::ResponseParts::send_buffer_size`] is the write-time HTTP/2 send buffer overlay. Distinct from [`{G}::ResponseParts::limits`]."
);
let _ = writeln!(
src,
" /// [`{G}::ResponseParts::compress_is_set`] is occupancy after a generated server on_response, so a later interceptor can fill compress only when unset."
);
let _ = writeln!(
src,
" /// [`{G}::ResponseParts::clear_compress`] restores the server gzip overlay after a generated server on_response."
);
let _ = writeln!(
src,
" /// [`{G}::Status::from_error_details`] is the typed bag after a generated server on_response Err; a local reject is trailers-only after handler Ok."
);
let _ = writeln!(
src,
" /// Distinct from a generated handler Err: that is after the handler ran; this generated server on_response Err is trailers-only after handler Ok."
);
let _ = writeln!(
src,
" /// Distinct from a generated server intercept Err: that is trailers without reading the body; this generated server on_response Err is trailers-only after handler Ok."
);
let _ = writeln!(
src,
" /// Distinct from a generated client on_response Err: that fails the Call after a successful receive; this generated server on_response Err is trailers-only after handler Ok."
);
let _ = writeln!(
src,
" /// Distinct from a generated StreamSender fail: that is trailers after any messages already sent; this generated server on_response Err is trailers-only after handler Ok."
);
let _ = writeln!(
src,
" /// Distinct from [`Self::intercept`]: that runs on the inbound RPC before the handler; this generated server on_response runs after the handler returns Ok."
);
let _ = writeln!(
src,
" /// Compiling overlay dumps live on [`{G}::hello`] (`GreeterServer::new(Svc).on_response`)."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn on_response<I>(self, interceptor: I) -> {G}::Server<Self>"
);
let _ = writeln!(src, " where");
let _ = writeln!(src, " I: {G}::ResponseInterceptor,");
let _ = writeln!(src, " {{");
let _ = writeln!(src, " self.into_server().on_response(interceptor)");
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" /// Mount alongside another service. [`Self::max_decoding_message_size`] and [`Self::max_encoding_message_size`] stay in effect on every mounted service, on every call shape of those mounts, including over TLS, mTLS, Unix, and [`{G}::Server::serve_connection`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn add_service<S: {G}::Service>(self, service: S) -> {G}::Router {{ self.into_server().add_service(service) }}"
);
let _ = writeln!(
src,
" /// Mount `service` when `Some`. `None` is a no-op. Distinct from [`Self::add_service`], which always mounts. `None` does not replace a service already there. Services that stay mounted still complete every call shape, including over TLS, mTLS, Unix, and [`{G}::Server::serve_connection`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn add_optional_service<S: {G}::Service>(self, service: ::core::option::Option<S>) -> {G}::Router {{ self.into_server().add_optional_service(service) }}"
);
let _ = writeln!(
src,
" /// Move this service into a [`{G}::Router`], keeping the configuration."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn into_router(self) -> {G}::Router {{ self.into_server().into_router() }}"
);
let _ = writeln!(
src,
" /// Bind `addr` and serve until the listener fails. Applies to every call shape."
);
let _ = writeln!(
src,
" pub async fn serve(self, addr: ::std::net::SocketAddr) -> ::core::result::Result<(), {G}::Status> {{ self.into_server().serve(addr).await }}"
);
let _ = writeln!(
src,
" /// Serve on an existing listener until it fails. Applies to every call shape."
);
let _ = writeln!(
src,
" pub async fn serve_listener(self, listener: {G}::codegen_support::TcpListener) -> ::core::result::Result<(), {G}::Status> {{ self.into_server().serve_listener(listener).await }}"
);
let _ = writeln!(
src,
" /// Serve until `shutdown` resolves, then drain. Applies to every call shape. In-flight RPCs finish; new connections are refused. TLS and Unix drain the same way (`serve_tls_with_shutdown`, `serve_unix_with_shutdown`)."
);
let _ = writeln!(
src,
" pub async fn serve_with_shutdown(self, listener: {G}::codegen_support::TcpListener, shutdown: impl ::core::future::Future<Output = ()> + Send) -> ::core::result::Result<(), {G}::Status> {{ self.into_server().serve_with_shutdown(listener, shutdown).await }}"
);
let _ = writeln!(
src,
" /// Bind `addr` and serve until `shutdown` resolves, then drain. Applies to every call shape."
);
let _ = writeln!(
src,
" pub async fn serve_until_shutdown(self, addr: ::std::net::SocketAddr, shutdown: impl ::core::future::Future<Output = ()> + Send) -> ::core::result::Result<(), {G}::Status> {{ self.into_server().serve_until_shutdown(addr, shutdown).await }}"
);
let _ = writeln!(
src,
" /// Bind `path` and serve h2c over a Unix domain socket. Applies to every call shape."
);
let _ = writeln!(src, " #[cfg(unix)]");
let _ = writeln!(
src,
" pub async fn serve_unix(self, path: impl AsRef<::std::path::Path>) -> ::core::result::Result<(), {G}::Status> {{ self.into_server().serve_unix(path).await }}"
);
let _ = writeln!(
src,
" /// Bind `path` after unlinking a crash leftover. A live listener is left alone and this fails with UNAVAILABLE. Applies to every call shape."
);
let _ = writeln!(src, " #[cfg(unix)]");
let _ = writeln!(
src,
" pub async fn serve_unix_unlink(self, path: impl AsRef<::std::path::Path>) -> ::core::result::Result<(), {G}::Status> {{ self.into_server().serve_unix_unlink(path).await }}"
);
let _ = writeln!(
src,
" /// Serve h2c on an existing Unix listener. Applies to every call shape."
);
let _ = writeln!(src, " #[cfg(unix)]");
let _ = writeln!(
src,
" pub async fn serve_unix_listener(self, listener: {G}::codegen_support::UnixListener) -> ::core::result::Result<(), {G}::Status> {{ self.into_server().serve_unix_listener(listener).await }}"
);
let _ = writeln!(
src,
" /// Serve h2c on a Unix listener until `shutdown` resolves, then drain. Applies to every call shape. In-flight RPCs finish; new connections are refused. See [`{G}::Server::serve_with_shutdown`]."
);
let _ = writeln!(src, " #[cfg(unix)]");
let _ = writeln!(
src,
" pub async fn serve_unix_with_shutdown(self, listener: {G}::codegen_support::UnixListener, shutdown: impl ::core::future::Future<Output = ()> + Send) -> ::core::result::Result<(), {G}::Status> {{ self.into_server().serve_unix_with_shutdown(listener, shutdown).await }}"
);
let _ = writeln!(
src,
" /// Bind `path` and serve h2c until `shutdown` resolves, then drain. Applies to every call shape."
);
let _ = writeln!(src, " #[cfg(unix)]");
let _ = writeln!(
src,
" pub async fn serve_unix_until_shutdown(self, path: impl AsRef<::std::path::Path>, shutdown: impl ::core::future::Future<Output = ()> + Send) -> ::core::result::Result<(), {G}::Status> {{ self.into_server().serve_unix_until_shutdown(path, shutdown).await }}"
);
let _ = writeln!(
src,
" /// Bind `path` until `shutdown` after unlinking a crash leftover. A live listener is left alone. Applies to every call shape."
);
let _ = writeln!(src, " #[cfg(unix)]");
let _ = writeln!(
src,
" pub async fn serve_unix_unlink_until_shutdown(self, path: impl AsRef<::std::path::Path>, shutdown: impl ::core::future::Future<Output = ()> + Send) -> ::core::result::Result<(), {G}::Status> {{ self.into_server().serve_unix_unlink_until_shutdown(path, shutdown).await }}"
);
let _ = writeln!(
src,
" /// Bind `addr` and serve over TLS until the listener fails. Applies to every call shape."
);
let _ = writeln!(
src,
" pub async fn serve_tls(self, addr: ::std::net::SocketAddr, tls: {G}::ServerTls) -> ::core::result::Result<(), {G}::Status> {{ self.into_server().serve_tls(addr, tls).await }}"
);
let _ = writeln!(
src,
" /// Serve over TLS until `shutdown` resolves, then drain. Applies to every call shape, including mTLS. In-flight RPCs finish; new connections are refused."
);
let _ = writeln!(
src,
" pub async fn serve_tls_with_shutdown(self, listener: {G}::codegen_support::TcpListener, shutdown: impl ::core::future::Future<Output = ()> + Send, tls: {G}::ServerTls) -> ::core::result::Result<(), {G}::Status> {{ self.into_server().serve_tls_with_shutdown(listener, shutdown, tls).await }}"
);
let _ = writeln!(
src,
" /// Bind `addr` and serve over TLS until `shutdown` resolves, then drain. Applies to every call shape."
);
let _ = writeln!(
src,
" pub async fn serve_tls_until_shutdown(self, addr: ::std::net::SocketAddr, shutdown: impl ::core::future::Future<Output = ()> + Send, tls: {G}::ServerTls) -> ::core::result::Result<(), {G}::Status> {{ self.into_server().serve_tls_until_shutdown(addr, shutdown, tls).await }}"
);
let _ = writeln!(
src,
" /// Serve a single already-accepted byte stream until it closes. Applies to every call shape. Generated handlers see empty peer facts on [`{G}::Request`] / [`{G}::Parts`]; [`{G}::Rpc::scheme`] is the peer's `:scheme`. See [`{G}::Server::serve_connection`]."
);
let _ = writeln!(
src,
" pub async fn serve_connection<IO>(self, io: IO) -> ::core::result::Result<(), {G}::Status>"
);
let _ = writeln!(src, " where");
let _ = writeln!(
src,
" IO: {G}::codegen_support::AsyncRead + {G}::codegen_support::AsyncWrite + Unpin + Send + 'static,"
);
let _ = writeln!(src, " {{");
let _ = writeln!(src, " self.into_server().serve_connection(io).await");
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" /// Serve connections from `incoming` until it is exhausted. Applies to every call shape. Override [`{G}::Incoming::peer`] to stamp connection facts."
);
let _ = writeln!(
src,
" pub async fn serve_with_incoming<I: {G}::Incoming>(self, incoming: I) -> ::core::result::Result<(), {G}::Status> {{ self.into_server().serve_with_incoming(incoming).await }}"
);
let _ = writeln!(
src,
" /// Serve from `incoming` until `shutdown` resolves, then drain. Applies to every call shape."
);
let _ = writeln!(
src,
" pub async fn serve_with_incoming_shutdown<I: {G}::Incoming>(self, incoming: I, shutdown: impl ::core::future::Future<Output = ()> + Send) -> ::core::result::Result<(), {G}::Status> {{ self.into_server().serve_with_incoming_shutdown(incoming, shutdown).await }}"
);
let _ = writeln!(
src,
" fn into_server(self) -> {G}::Server<Self> {{ let config = self.config; {G}::Server::new(self).config(config) }}"
);
let _ = writeln!(src, "}}");
let _ = writeln!(src, "impl<T: {trait_name}> {G}::Service for {server}<T> {{");
let _ = writeln!(src, " const NAME: &'static str = \"{full_name}\";");
if full_name == "grpc.reflection.v1.ServerReflection" {
let _ = writeln!(
src,
" /// Path alias for older grpcurl that falls back to `grpc.reflection.v1alpha`."
);
let _ = writeln!(
src,
" /// Same handler as [`Self::NAME`]. Not a second proto."
);
let _ = writeln!(
src,
" const ALIASES: &'static [&'static str] = &[\"grpc.reflection.v1alpha.ServerReflection\"];"
);
}
let _ = writeln!(
src,
" fn call(&self, rpc: {G}::Rpc) -> impl ::core::future::Future<Output = ()> + Send {{"
);
let _ = writeln!(
src,
" let inner = ::std::sync::Arc::clone(&self.inner);"
);
let _ = writeln!(src, " async move {{");
let _ = writeln!(src, " match rpc.method() {{");
for m in &svc.methods {
let shape = shape_of(m);
let fn_name = to_snake(&m.name);
let _ = writeln!(src, " \"{}\" => {{", m.name);
let _ = writeln!(
src,
" rpc.{}(move |request| async move {{ inner.{fn_name}(request).await }}).await;",
shape.dispatch
);
let _ = writeln!(src, " }}");
}
let _ = writeln!(src, " _ => rpc.unimplemented(),");
let _ = writeln!(src, " }}");
let _ = writeln!(src, " }}");
let _ = writeln!(src, " }}");
let _ = writeln!(src, "}}");
}
fn emit_kernel_client_dialers(src: &mut String) {
let _ = writeln!(
src,
" /// Dial `target` (`host:port`, not a tonic `http://` / `https://` URI). See [`{G}::Channel::connect`]. Applies to every call shape."
);
let _ = writeln!(
src,
" pub async fn connect(target: impl Into<{G}::Target>) -> ::core::result::Result<Self, {G}::Status> {{"
);
let _ = writeln!(
src,
" Ok(Self::new({G}::Channel::connect(target).await?))"
);
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" /// [`Self::connect`] with [`{G}::ChannelConfig`]. Applies to every call shape."
);
let _ = writeln!(
src,
" pub async fn connect_with(target: impl Into<{G}::Target>, config: {G}::ChannelConfig) -> ::core::result::Result<Self, {G}::Status> {{"
);
let _ = writeln!(
src,
" Ok(Self::new({G}::Channel::connect_with(target, config).await?))"
);
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" /// Open `connections` slots. See [`{G}::Channel::connect_pool`]. Applies to every call shape."
);
let _ = writeln!(
src,
" /// TLS (including mTLS) pooling is [`Self::connect_tls_with`] plus [`{G}::ChannelConfig::connections`]; Unix is [`Self::connect_unix_with`]. [`{G}::Channel::from_io`] cannot pool."
);
let _ = writeln!(
src,
" /// A pool larger than the server's [`{G}::Server::max_concurrent_connections`] fails the dial as `UNAVAILABLE`."
);
let _ = writeln!(
src,
" pub async fn connect_pool(target: impl Into<{G}::Target>, connections: usize) -> ::core::result::Result<Self, {G}::Status> {{"
);
let _ = writeln!(
src,
" Ok(Self::new({G}::Channel::connect_pool(target, connections).await?))"
);
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" /// Dial over TLS. See [`{G}::Channel::connect_tls`]. Applies to every call shape."
);
let _ = writeln!(
src,
" pub async fn connect_tls(target: impl Into<{G}::Target>, tls: {G}::ClientTls) -> ::core::result::Result<Self, {G}::Status> {{"
);
let _ = writeln!(
src,
" Ok(Self::new({G}::Channel::connect_tls(target, tls).await?))"
);
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" /// [`Self::connect_tls`] with [`{G}::ChannelConfig`]. Applies to every call shape."
);
let _ = writeln!(
src,
" /// [`{G}::ChannelConfig::connections`] opens that many TLS sockets (including mTLS); all must succeed. [`{G}::Channel::from_io`] cannot pool."
);
let _ = writeln!(
src,
" pub async fn connect_tls_with(target: impl Into<{G}::Target>, config: {G}::ChannelConfig, tls: {G}::ClientTls) -> ::core::result::Result<Self, {G}::Status> {{"
);
let _ = writeln!(
src,
" Ok(Self::new({G}::Channel::connect_tls_with(target, config, tls).await?))"
);
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" /// Dial on the first RPC. See [`{G}::Channel::connect_lazy`]. Applies to every call shape."
);
let _ = writeln!(
src,
" pub fn connect_lazy(target: impl Into<{G}::Target>) -> ::core::result::Result<Self, {G}::Status> {{"
);
let _ = writeln!(
src,
" Ok(Self::new({G}::Channel::connect_lazy(target)?))"
);
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" /// [`Self::connect_lazy`] with [`{G}::ChannelConfig`]. Applies to every call shape."
);
let _ = writeln!(
src,
" pub fn connect_lazy_with(target: impl Into<{G}::Target>, config: {G}::ChannelConfig) -> ::core::result::Result<Self, {G}::Status> {{"
);
let _ = writeln!(
src,
" Ok(Self::new({G}::Channel::connect_lazy_with(target, config)?))"
);
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" /// [`Self::connect_lazy`] over TLS. See [`{G}::Channel::connect_tls_lazy`]. Applies to every call shape."
);
let _ = writeln!(
src,
" pub fn connect_tls_lazy(target: impl Into<{G}::Target>, tls: {G}::ClientTls) -> ::core::result::Result<Self, {G}::Status> {{"
);
let _ = writeln!(
src,
" Ok(Self::new({G}::Channel::connect_tls_lazy(target, tls)?))"
);
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" /// [`Self::connect_tls_lazy`] with [`{G}::ChannelConfig`]. Applies to every call shape."
);
let _ = writeln!(
src,
" pub fn connect_tls_lazy_with(target: impl Into<{G}::Target>, config: {G}::ChannelConfig, tls: {G}::ClientTls) -> ::core::result::Result<Self, {G}::Status> {{"
);
let _ = writeln!(
src,
" Ok(Self::new({G}::Channel::connect_tls_lazy_with(target, config, tls)?))"
);
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" /// Dial a Unix domain socket. See [`{G}::Channel::connect_unix`]. Applies to every call shape."
);
let _ = writeln!(src, " #[cfg(unix)]");
let _ = writeln!(
src,
" pub async fn connect_unix(path: impl AsRef<::std::path::Path>) -> ::core::result::Result<Self, {G}::Status> {{"
);
let _ = writeln!(
src,
" Ok(Self::new({G}::Channel::connect_unix(path).await?))"
);
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" /// [`Self::connect_unix`] with [`{G}::ChannelConfig`]. Applies to every call shape."
);
let _ = writeln!(
src,
" /// [`{G}::ChannelConfig::connections`] opens that many Unix sockets; all must succeed. [`{G}::Channel::from_io`] cannot pool."
);
let _ = writeln!(src, " #[cfg(unix)]");
let _ = writeln!(
src,
" pub async fn connect_unix_with(path: impl AsRef<::std::path::Path>, config: {G}::ChannelConfig) -> ::core::result::Result<Self, {G}::Status> {{"
);
let _ = writeln!(
src,
" Ok(Self::new({G}::Channel::connect_unix_with(path, config).await?))"
);
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" /// [`Self::connect_unix`] that dials on the first RPC. Applies to every call shape."
);
let _ = writeln!(src, " #[cfg(unix)]");
let _ = writeln!(
src,
" pub fn connect_unix_lazy(path: impl AsRef<::std::path::Path>) -> ::core::result::Result<Self, {G}::Status> {{"
);
let _ = writeln!(
src,
" Ok(Self::new({G}::Channel::connect_unix_lazy(path)?))"
);
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" /// [`Self::connect_unix_lazy`] with [`{G}::ChannelConfig`]. Applies to every call shape."
);
let _ = writeln!(src, " #[cfg(unix)]");
let _ = writeln!(
src,
" pub fn connect_unix_lazy_with(path: impl AsRef<::std::path::Path>, config: {G}::ChannelConfig) -> ::core::result::Result<Self, {G}::Status> {{"
);
let _ = writeln!(
src,
" Ok(Self::new({G}::Channel::connect_unix_lazy_with(path, config)?))"
);
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" /// Speak gRPC over an already-connected byte stream. See [`{G}::Channel::from_io`]. Applies to every call shape."
);
let _ = writeln!(
src,
" pub async fn from_io<IO>(io: IO, authority: impl Into<{G}::Target>) -> ::core::result::Result<Self, {G}::Status>"
);
let _ = writeln!(src, " where");
let _ = writeln!(
src,
" IO: {G}::codegen_support::AsyncRead + {G}::codegen_support::AsyncWrite + Unpin + Send + 'static,"
);
let _ = writeln!(src, " {{");
let _ = writeln!(
src,
" Ok(Self::new({G}::Channel::from_io(io, authority).await?))"
);
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" /// [`Self::from_io`] with [`{G}::ChannelConfig`]. Applies to every call shape."
);
let _ = writeln!(
src,
" /// [`{G}::ChannelConfig::connections`] is forced to 1: one duplex is one HTTP/2 connection."
);
let _ = writeln!(
src,
" pub async fn from_io_with<IO>(io: IO, authority: impl Into<{G}::Target>, config: {G}::ChannelConfig) -> ::core::result::Result<Self, {G}::Status>"
);
let _ = writeln!(src, " where");
let _ = writeln!(
src,
" IO: {G}::codegen_support::AsyncRead + {G}::codegen_support::AsyncWrite + Unpin + Send + 'static,"
);
let _ = writeln!(src, " {{");
let _ = writeln!(
src,
" Ok(Self::new({G}::Channel::from_io_with(io, authority, config).await?))"
);
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" /// Send `:scheme https` from a [`Self::from_io`] channel. See [`{G}::Channel::https_scheme`]. Applies to every call shape."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn https_scheme(self) -> Self {{ Self {{ channel: self.channel.https_scheme() }} }}"
);
let _ = writeln!(
src,
" /// Override `:authority` on this clone without changing the dial. See [`{G}::Channel::origin`]. Applies to every call shape."
);
let _ = writeln!(
src,
" pub fn origin(self, authority: impl Into<{G}::Target>) -> ::core::result::Result<Self, {G}::Status> {{"
);
let _ = writeln!(
src,
" Ok(Self {{ channel: self.channel.origin(authority)? }})"
);
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" /// HTTP/2 `:scheme` this client sends. See [`{G}::Channel::scheme`]. Applies to every call shape."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn scheme(&self) -> &'static str {{ self.channel.scheme() }}"
);
let _ = writeln!(
src,
" /// The HTTP/2 `:authority` this client sends. See [`{G}::Channel::authority`]. Applies to every call shape."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn authority(&self) -> &str {{ self.channel.authority() }}"
);
let _ = writeln!(
src,
" /// The `user-agent` this client sends. See [`{G}::Channel::grpc_user_agent`]. Applies to every call shape."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn grpc_user_agent(&self) -> &str {{ self.channel.grpc_user_agent() }}"
);
let _ = writeln!(
src,
" /// The configuration in effect. See [`{G}::Channel::config`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn config(&self) -> {G}::ChannelConfig {{ self.channel.config() }}"
);
let _ = writeln!(
src,
" /// Whether any pool slot currently holds a live HTTP/2 connection. Distinct from gRPC `GetState`. See [`{G}::Channel::connected`]. Applies to every call shape. Client interceptors see the same snapshot as [`{G}::Outgoing::connected`]. Distinct from [`Self::wait_for_ready`]: that overlay queues; this getter is a live snapshot."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn connected(&self) -> bool {{ self.channel.connected() }}"
);
let _ = writeln!(
src,
" /// Default per-RPC deadline when the request omits one. Distinct from [`Self::timeout`], which sets it. See [`{G}::Channel::rpc_timeout`]. Applies to every call shape."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn rpc_timeout(&self) -> ::core::option::Option<::std::time::Duration> {{ self.channel.rpc_timeout() }}"
);
let _ = writeln!(
src,
" /// Whether this client waits for a connection instead of failing fast. See [`{G}::Channel::waits_for_ready`]. Applies to every call shape. Distinct from [`Self::connected`]: that getter is a live snapshot, not this overlay."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn waits_for_ready(&self) -> bool {{ self.channel.waits_for_ready() }}"
);
let _ = writeln!(
src,
" /// Whether this client gzips outbound payloads. See [`{G}::Channel::compresses_outbound`]. Applies to every call shape."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn compresses_outbound(&self) -> bool {{ self.channel.compresses_outbound() }}"
);
let _ = writeln!(
src,
" /// Configured outbound gzip deflate level. Distinct from [`Self::gzip_compression_level`], which sets it. See [`{G}::Channel::gzip_level`]. Applies to every call shape."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn gzip_level(&self) -> u32 {{ self.channel.gzip_level() }}"
);
let _ = writeln!(
src,
" /// Whether inbound gzip is inflated. Default `true`. Distinct from [`Self::accept_compressed`], which sets it. Distinct from [`{G}::Rpc::accepts_gzip`], which is the peer's `grpc-accept-encoding`. See [`{G}::Channel::accepts_compressed`]. Applies to every call shape."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn accepts_compressed(&self) -> bool {{ self.channel.accepts_compressed() }}"
);
let _ = writeln!(
src,
" /// Configured channel-wide RPC cap, if any. Distinct from [`Self::max_concurrent_rpcs`], which sets it. See [`{G}::Channel::concurrent_rpc_limit`]. Applies to every call shape."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn concurrent_rpc_limit(&self) -> ::core::option::Option<usize> {{ self.channel.concurrent_rpc_limit() }}"
);
let _ = writeln!(
src,
" /// Configured outbound streaming queue depth. Distinct from [`Self::stream_buffer`], which sets it. Distinct from [`Self::message_limits`]: that is message size, not queue depth. See [`{G}::Channel::stream_buffer_size`]. Applies to client-streaming and bidi request streams."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn stream_buffer_size(&self) -> usize {{ self.channel.stream_buffer_size() }}"
);
let _ = writeln!(
src,
" /// Configured write-time HTTP/2 send buffer. Distinct from [`Self::max_send_buffer_size`], which sets it. Distinct from [`Self::stream_buffer_size`]: that is queue depth, not this send buffer. See [`{G}::Channel::send_buffer_size`]. Applies to every call shape."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn send_buffer_size(&self) -> usize {{ self.channel.send_buffer_size() }}"
);
let _ = writeln!(
src,
" /// Configured message caps. Distinct from [`Self::message_limits`], which sets them. Distinct from [`Self::stream_buffer_size`]: that is queue depth, not uncompressed protobuf bytes. Distinct from [`Self::send_buffer_size`]: that is the HTTP/2 send buffer, not these caps. See [`{G}::Channel::limits`]. Applies to every call shape."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn limits(&self) -> {G}::MessageLimits {{ self.channel.limits() }}"
);
}
fn emit_kernel_client(
src: &mut String,
trait_name: &str,
client: &str,
full_name: &str,
svc: &ServiceDescriptor,
) {
let _ = writeln!(src, "/// Client for `{full_name}`.");
let _ = writeln!(src, "///");
let _ = writeln!(
src,
"/// Wraps a [`{G}::Channel`]; cloning is cheap and shares connections."
);
let _ = writeln!(
src,
"/// Dial with [`Self::connect`], [`Self::connect_tls`], or [`Self::from_io`]."
);
let _ = writeln!(
src,
"/// On Unix, `connect_unix` takes a filesystem path. Wrap an existing channel with [`Self::new`]."
);
let _ = writeln!(
src,
"/// [`Self::authority`], [`Self::scheme`], and [`Self::grpc_user_agent`] read the same values interceptors see on [`{G}::Outgoing`]. [`Self::config`] is the channel overlay those values come from. [`Self::rpc_timeout`], [`Self::waits_for_ready`], [`Self::compresses_outbound`], [`Self::gzip_level`], [`Self::accepts_compressed`], [`Self::concurrent_rpc_limit`], [`Self::stream_buffer_size`], [`Self::send_buffer_size`], and [`Self::limits`] read that overlay without colliding with the setters."
);
let _ = writeln!(
src,
"/// [`Self::connected`] is the live-socket snapshot. Distinct from [`Self::waits_for_ready`]. Same snapshot as [`{G}::Channel::connected`]."
);
let _ = writeln!(src, "#[derive(::core::clone::Clone)]");
let _ = writeln!(src, "pub struct {client} {{");
let _ = writeln!(src, " channel: {G}::Channel,");
let _ = writeln!(src, "}}");
let _ = writeln!(src, "impl ::core::fmt::Debug for {client} {{");
let _ = writeln!(
src,
" fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {{"
);
let _ = writeln!(
src,
" f.debug_struct(\"{client}\").field(\"channel\", &self.channel).finish()"
);
let _ = writeln!(src, " }}");
let _ = writeln!(src, "}}");
let _ = writeln!(src, "impl {client} {{");
let _ = writeln!(src, " /// Fully qualified proto service name.");
let _ = writeln!(src, " pub const NAME: &'static str = \"{full_name}\";");
let _ = writeln!(src, " /// Wrap a channel.");
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn new(channel: {G}::Channel) -> Self {{ Self {{ channel }} }}"
);
let _ = writeln!(src, " /// Take the channel back.");
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn into_inner(self) -> {G}::Channel {{ self.channel }}"
);
emit_kernel_client_dialers(src);
let _ = writeln!(
src,
" /// Cap inbound messages at `limit` bytes. Default 4 MiB. Applies to every call shape, including over TLS, mTLS, Unix, and [`{G}::Channel::from_io`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn max_decoding_message_size(mut self, limit: usize) -> Self {{ self.channel = self.channel.max_decoding_message_size(limit); self }}"
);
let _ = writeln!(
src,
" /// Cap outbound messages at `limit` bytes. Default unlimited. Applies to every call shape, including over TLS, mTLS, Unix, and [`{G}::Channel::from_io`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn max_encoding_message_size(mut self, limit: usize) -> Self {{ self.channel = self.channel.max_encoding_message_size(limit); self }}"
);
let _ = writeln!(
src,
" /// Replace both message caps at once. Applies to every call shape. See [`{G}::Channel::message_limits`]."
);
let _ = writeln!(
src,
" /// Distinct from [`Self::max_encoding_message_size`] / [`Self::max_decoding_message_size`]. Oversize is `RESOURCE_EXHAUSTED` on every call shape, including over TLS, mTLS, Unix, and [`{G}::Channel::from_io`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn message_limits(mut self, limits: {G}::MessageLimits) -> Self {{ self.channel = self.channel.message_limits(limits); self }}"
);
let _ = writeln!(
src,
" /// Run `interceptor` on every outbound RPC before the stream opens. Invoked when the generated method is called, not on first poll of the [`{G}::Call`]. `Err` fails that Call on poll, including [`{G}::Status::with_error_details`]; nothing is sent. A local [`{G}::Status::with_error_details`] is [`{G}::Status::rpc`] / [`{G}::Status::error_details`] on that Call for every call shape. [`{G}::Outgoing::set_timeout`] is that Call's deadline on every call shape. `clear_timeout` opts out of the channel timeout on every call shape. `clear_compress` then `set_compress(compresses_outbound())` reapplies channel gzip on every call shape. `set_compress` stamps [`{G}::StreamSender::compress`] on client-streaming and bidi request streams. Outgoing getters apply to every call shape. `clear_user_agent` restores the channel user-agent after a generated intercept prefix."
);
let _ = writeln!(
src,
" /// The interceptor sees a [`{G}::Outgoing`]: path, service, method, `:authority`, `:scheme`,"
);
let _ = writeln!(
src,
" /// `user-agent` (`user_agent_is_set`), message caps, metadata, timeout / deadline Instant, wait-for-ready (`wait_for_ready_is_set`), compression (`compress_is_set`), channel overlays (`rpc_timeout` / `waits_for_ready` / `compresses_outbound` / `gzip_level` / `accepts_compressed` / `concurrent_rpc_limit` / `stream_buffer_size` / `send_buffer_size` / `limits`; `clear_*` opts out of the already-applied default), extensions, `connected` (same snapshot as [`{G}::Channel::connected`]). [`{G}::Outgoing::set_user_agent`] prefixes this RPC. [`{G}::Request::set_user_agent`] is the same prefix at the call site. [`{G}::Outgoing::connected`] is the live-socket snapshot. Distinct from wait-for-ready: a lazy first RPC sees `false` even when that overlay is on. [`{G}::Outgoing::gzip_level`] is deflate effort. Distinct from [`{G}::Outgoing::compresses_outbound`] (on or off). An interceptor cannot change it. [`{G}::Outgoing::accepts_compressed`] is the inbound gzip overlay (default on). [`{G}::Outgoing::limits`] is the channel message-cap overlay. Same overlay as [`{G}::Channel::limits`]. [`{G}::Outgoing::concurrent_rpc_limit`] is the channel RPC cap overlay. Distinct from [`{G}::Outgoing::waits_for_ready`]: that waits for a connection; this refuses extras. [`{G}::Outgoing::stream_buffer_size`] is the outbound streaming queue overlay. Distinct from [`{G}::Outgoing::limits`]: that is message size, not queue depth. [`{G}::Outgoing::send_buffer_size`] is the outbound HTTP/2 send buffer overlay. Distinct from [`{G}::Outgoing::stream_buffer_size`]: that is queue depth, not this send buffer."
);
let _ = writeln!(
src,
" /// Compiling overlay dumps live on [`{G}::hello`] (`GreeterClient::new(channel).intercept`)."
);
let _ = writeln!(
src,
" /// [`{G}::Outgoing::user_agent_is_set`] is occupancy on this generated intercept path, so a later interceptor can prefix only when unset."
);
let _ = writeln!(
src,
" /// [`{G}::Outgoing::wait_for_ready_is_set`] is occupancy on this generated intercept path, so a later interceptor can fill wait-for-ready only when unset."
);
let _ = writeln!(
src,
" /// [`{G}::Outgoing::compress_is_set`] is occupancy on this generated intercept path, so a later interceptor can fill compress only when unset."
);
let _ = writeln!(
src,
" /// `clear_wait_for_ready` restores the channel wait-for-ready overlay after a generated intercept choice."
);
let _ = writeln!(
src,
" /// `clear_timeout` opts out of the channel timeout after a generated intercept choice."
);
let _ = writeln!(
src,
" /// `clear_compress` then `set_compress` from `compresses_outbound` reapplies channel gzip after a generated intercept choice."
);
let _ = writeln!(
src,
" /// [`{G}::Outgoing::connected`] is the live-socket snapshot on this generated intercept path ([`{G}::Channel::connected`]), taken when the interceptor runs. Distinct from wait-for-ready: a lazy first RPC sees `false` even when that overlay is on."
);
let _ = writeln!(
src,
" /// [`{G}::Status::from_error_details`] is the typed bag after a generated intercept Err; a local reject never opens a stream."
);
let _ = writeln!(
src,
" /// Distinct from a generated handler Err: that is after the handler ran; this generated intercept Err is a local reject never opens a stream."
);
let _ = writeln!(
src,
" /// Distinct from a generated client on_response Err: that fails the Call after a successful receive; this generated intercept Err is a local reject never opens a stream."
);
let _ = writeln!(
src,
" /// Distinct from a generated server intercept Err: that is trailers without reading the body; this generated intercept Err is a local reject never opens a stream."
);
let _ = writeln!(
src,
" /// Distinct from a generated StreamSender fail: that is trailers after any messages already sent; this generated intercept Err is a local reject never opens a stream."
);
let _ = writeln!(
src,
" /// Distinct from [`{G}::Channel::max_concurrent_rpcs`]: that takes a slot when the [`{G}::Call`] is polled; a generated intercept already ran, so a local Err never consumes that budget."
);
let _ = writeln!(
src,
" /// Distinct from [`{G}::Server::intercept`]: that runs on the inbound RPC before the handler; this generated intercept runs on the outbound call before the stream opens."
);
let _ = writeln!(
src,
" /// Distinct from [`Self::on_response`]: that runs after a successful receive; this generated intercept runs on the outbound call before the stream opens."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(src, " pub fn intercept<I>(self, interceptor: I) -> Self");
let _ = writeln!(src, " where");
let _ = writeln!(src, " I: {G}::ClientInterceptor,");
let _ = writeln!(src, " {{");
let _ = writeln!(
src,
" Self {{ channel: self.channel.intercept(interceptor) }}"
);
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" /// Run `interceptor` after a successful receive, before the [`{G}::Call`] is Ready. Closures implement [`{G}::ResponseInterceptor`]. A received reply starts empty; this is how a client inserts typed context after the peer cannot. Distinct from [`Self::intercept`], which runs before the stream opens. Calling this twice stacks: the first interceptor runs first. Applies to every call shape, including over TLS, mTLS, Unix, and [`{G}::Channel::from_io`]. `Err` fails that Call (the peer already sent OK), including [`{G}::Status::with_error_details`]. A non-OK peer status skips this hook. On server-streaming and bidi, trailers on this envelope do not replace [`{G}::Streaming::trailers`]. See [`{G}::Channel::on_response`]. [`{G}::ResponseParts::path`] is kernel-stamped. Distinct from [`{G}::Outgoing::path`]. [`{G}::Response::gzip_level`] on a received reply is not the peer's deflate effort. Distinct from [`{G}::Response::encoding`]. [`{G}::Response::compresses_outbound`] on a received reply is `false` (the overlay is not on the wire). [`{G}::Response::accepts_gzip`] on a received reply is `false` (the advertisement is not on the reply wire). [`{G}::Response::deadline`] on a received reply is `None` (the peer deadline is not on the wire). [`{G}::Response::timeout`] on a received reply is `None` (the peer timeout is not on the reply wire). [`{G}::Response::limits`] on a received reply is `None` (the peer encode cap is not on the wire). [`{G}::Response::peer_timeout`] on a received reply is `None` (the client's `grpc-timeout` is not on the reply wire). [`{G}::Response::rpc_timeout`] on a received reply is `None` (the server overlay is not on the reply wire). [`{G}::Response::accepts_compressed`] on a received reply is `false` (this overlay is not a received-reply field). [`{G}::Response::send_buffer_size`] on a received reply is `None` (the peer send buffer is not on the reply wire)."
);
let _ = writeln!(
src,
" /// [`{G}::ResponseParts::compress_is_set`] is occupancy after a generated client on_response, so a later interceptor can fill compress only when unset."
);
let _ = writeln!(
src,
" /// [`{G}::ResponseParts::clear_compress`] drops a compress choice after a generated client on_response; a received reply has no server gzip overlay to restore."
);
let _ = writeln!(
src,
" /// [`{G}::Status::from_error_details`] is the typed bag after a generated client on_response Err; a local reject fails the Call after a successful receive."
);
let _ = writeln!(
src,
" /// Distinct from a generated handler Err: that is after the handler ran; this generated client on_response Err fails the Call after a successful receive."
);
let _ = writeln!(
src,
" /// Distinct from a generated server intercept Err: that is trailers without reading the body; this generated client on_response Err fails the Call after a successful receive."
);
let _ = writeln!(
src,
" /// Distinct from a generated intercept Err: that is a local reject never opens a stream; this generated client on_response Err fails the Call after a successful receive."
);
let _ = writeln!(
src,
" /// Distinct from a generated server on_response Err: that is trailers-only after handler Ok; this generated client on_response Err fails the Call after a successful receive."
);
let _ = writeln!(
src,
" /// Distinct from a generated StreamSender fail: that is trailers after any messages already sent; this generated client on_response Err fails the Call after a successful receive."
);
let _ = writeln!(
src,
" /// Distinct from [`Self::intercept`]: that runs on the outbound call before the stream opens; this generated client on_response runs after a successful receive."
);
let _ = writeln!(
src,
" /// Compiling overlay dumps live on [`{G}::hello`] (`GreeterClient::new(channel).on_response`)."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn on_response<I>(self, interceptor: I) -> Self"
);
let _ = writeln!(src, " where");
let _ = writeln!(src, " I: {G}::ResponseInterceptor,");
let _ = writeln!(src, " {{");
let _ = writeln!(
src,
" Self {{ channel: self.channel.on_response(interceptor) }}"
);
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" /// Prefix the kernel `user-agent`. See [`{G}::Channel::user_agent`]. Applies to every call shape."
);
let _ = writeln!(
src,
" pub fn user_agent(self, prefix: impl AsRef<str>) -> ::core::result::Result<Self, {G}::Status> {{"
);
let _ = writeln!(
src,
" Ok(Self {{ channel: self.channel.user_agent(prefix)? }})"
);
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" /// gzip unary and server-streaming request payloads and [`{G}::StreamSender::send`]. Applies to every call shape, including over TLS, mTLS, Unix, and [`{G}::Channel::from_io`]. See [`{G}::Channel::send_compressed`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn send_compressed(mut self) -> Self {{ self.channel = self.channel.send_compressed(); self }}"
);
let _ = writeln!(
src,
" /// Deflate effort for outbound gzip. Default 1 (`flate2` fast). Applies to every call shape, including over TLS, mTLS, Unix, and [`{G}::Channel::from_io`]. Distinct from [`Self::send_compressed`], which is on or off. 0 stores; 9 is best. See [`{G}::Channel::gzip_compression_level`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn gzip_compression_level(mut self, level: u32) -> Self {{ self.channel = self.channel.gzip_compression_level(level); self }}"
);
let _ = writeln!(
src,
" /// Inflate inbound gzip. Default `true`. Applies to every call shape, including over TLS, mTLS, Unix, and [`{G}::Channel::from_io`]. Passing `false` omits gzip from `grpc-accept-encoding` and refuses a gzip reply as `UNIMPLEMENTED`. Distinct from [`Self::send_compressed`], which is outbound. See [`{G}::Channel::accept_compressed`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn accept_compressed(mut self, accept: bool) -> Self {{ self.channel = self.channel.accept_compressed(accept); self }}"
);
let _ = writeln!(
src,
" /// Default per-RPC deadline when the request omits one. See [`{G}::Channel::timeout`]. Applies to every call shape, including over TLS, mTLS, Unix, and [`{G}::Channel::from_io`]. Interceptors run after this fill and can still set or [`{G}::Outgoing::clear_timeout`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn timeout(mut self, timeout: ::std::time::Duration) -> Self {{ self.channel = self.channel.timeout(timeout); self }}"
);
let _ = writeln!(
src,
" /// Wait for a connection instead of failing fast. See [`{G}::Channel::wait_for_ready`]. Applies to every call shape."
);
let _ = writeln!(
src,
" /// A request that already called [`{G}::Request::set_wait_for_ready`] is left alone. Interceptors run after this fill and can still set or clear it."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn wait_for_ready(mut self) -> Self {{ self.channel = self.channel.wait_for_ready(); self }}"
);
let _ = writeln!(
src,
" /// How many messages sit between a client-streaming caller and the wire. See [`{G}::Channel::stream_buffer`]. Applies to client-streaming and bidi request streams."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn stream_buffer(mut self, messages: usize) -> Self {{ self.channel = self.channel.stream_buffer(messages); self }}"
);
let _ = writeln!(
src,
" /// Write-time HTTP/2 send buffer threshold for outbound DATA. See [`{G}::Channel::max_send_buffer_size`]. Applies to every call shape, including over TLS, mTLS, Unix, and [`{G}::Channel::from_io`]. Overlay: does not change how a dead slot is redialed."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn max_send_buffer_size(mut self, bytes: usize) -> Self {{ self.channel = self.channel.max_send_buffer_size(bytes); self }}"
);
let _ = writeln!(
src,
" /// Cap how many RPCs this channel will run at once. Applies to every call shape, including over TLS, mTLS, Unix, and [`{G}::Channel::from_io`]. Extra RPCs are `RESOURCE_EXHAUSTED` before the stream opens. Distinct from HTTP/2 `SETTINGS_MAX_CONCURRENT_STREAMS`, which waits. Distinct from [`{G}::Server::max_concurrent_rpcs`], which refuses inbound. Clones share the budget. See [`{G}::Channel::max_concurrent_rpcs`]."
);
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn max_concurrent_rpcs(mut self, n: usize) -> Self {{ self.channel = self.channel.max_concurrent_rpcs(n); self }}"
);
let _ = writeln!(src, " /// The channel this client sends on.");
let _ = writeln!(src, " #[must_use]");
let _ = writeln!(
src,
" pub fn channel(&self) -> &{G}::Channel {{ &self.channel }}"
);
for m in &svc.methods {
let shape = shape_of(m);
let fn_name = to_snake(&m.name);
let path = format!("/{full_name}/{}", m.name);
let _ = writeln!(src, " /// {} `{}`.", shape.description, m.name);
let _ = writeln!(
src,
" /// Interceptors run when this method is called, not on first poll of the [`{G}::Call`]."
);
let _ = writeln!(
src,
" /// The reply's [`{G}::Response::encoding`] is the peer's `grpc-encoding` (`None` for identity). The [`{G}::Call`] is fused after it resolves."
);
if !m.client_streaming && !m.server_streaming {
let _ = writeln!(
src,
" /// A [`{G}::CallHandle`] taken before await cancels the RPC."
);
let _ = writeln!(
src,
" /// OK-path custom trailers land on [`{G}::Response::trailers`]; a `-bin` trailer must not appear as a header, including over TLS, mTLS, Unix, and [`{G}::Channel::from_io`]."
);
}
if m.server_streaming {
let _ = writeln!(
src,
" /// Dropping the received [`{G}::Streaming`] before the end resets the RPC."
);
let _ = writeln!(
src,
" /// After end-of-stream or error that stream is fused (`{G}::FusedStream`)."
);
let _ = writeln!(
src,
" /// A [`{G}::CallHandle`] taken before await still cancels while waiting for headers, and still cancels that live stream after headers."
);
let _ = writeln!(
src,
" /// Letting the deadline fire RSTs the send half before headers and after headers the same way."
);
let _ = writeln!(
src,
" /// [`{G}::Streaming::trailers`] waits for end-of-stream, including when called before draining messages. A non-OK trailing `grpc-status` is `Err`. A `-bin` trailer must not appear as a header, including over TLS, mTLS, Unix, and [`{G}::Channel::from_io`]."
);
}
if m.client_streaming && !m.server_streaming {
let _ = writeln!(
src,
" /// A [`{G}::CallHandle`] taken before await still cancels after the sender is closed, while the unary response is pending."
);
let _ = writeln!(
src,
" /// Cancelling before any request message (`cancel_after_begin`) is [`{G}::Code::Cancelled`], not OK from a half-close: hold the [`{G}::StreamSender`] until the [`{G}::Call`] settles, including over TLS, mTLS, Unix, and [`{G}::Channel::from_io`]."
);
let _ = writeln!(
src,
" /// Dropping the [`{G}::Call`] or letting its deadline fire after that half-close resets the same way."
);
let _ = writeln!(
src,
" /// OK-path custom trailers land on [`{G}::Response::trailers`]; a `-bin` trailer must not appear as a header, including over TLS, mTLS, Unix, and [`{G}::Channel::from_io`]."
);
let _ = writeln!(
src,
" /// [`{G}::StreamSender::fail`] resolves the [`{G}::Call`] with that status (no request-side `grpc-status`; the stream is reset with CANCEL)."
);
}
if m.client_streaming && m.server_streaming {
let _ = writeln!(
src,
" /// [`{G}::StreamSender::fail`] before headers resolves the [`{G}::Call`] with that status, not `UNAVAILABLE` from the reset; after headers the received [`{G}::Streaming`] sees [`{G}::Code::Cancelled`], not that status."
);
}
if shape.client_return.starts_with('(') {
let _ = writeln!(
src,
" /// Dropping the pair without awaiting the [`{G}::Call`] resets the stream."
);
let _ = writeln!(
src,
" #[must_use = \"dropping a streaming Call resets the stream\"]"
);
}
let _ = writeln!(
src,
" pub fn {fn_name}(&self, request: {}) -> {} {{",
shape.client_arg, shape.client_return
);
let _ = writeln!(
src,
" self.channel.{}(\"{path}\", request)",
shape.client_call
);
let _ = writeln!(src, " }}");
}
let _ = writeln!(src, "}}");
let _ = writeln!(
src,
"impl ::core::convert::From<{G}::Channel> for {client} {{"
);
let _ = writeln!(
src,
" fn from(channel: {G}::Channel) -> Self {{ Self::new(channel) }}"
);
let _ = writeln!(src, "}}");
let _ = writeln!(src, "// `{trait_name}` is the server-side counterpart.");
}
fn emit_service(src: &mut String, svc: &ServiceDescriptor) {
let svc_ty = rust_ident(&svc.full_name);
let client = format!("{svc_ty}Client");
let server = format!("{svc_ty}Server");
let path_prefix = format!("/{}", svc.full_name);
let _ = writeln!(src, "pub trait {svc_ty}: Send + Sync + 'static {{");
for m in &svc.methods {
emit_service_trait_method(src, m);
}
let _ = writeln!(src, "}}");
let _ = writeln!(src, "#[derive(Clone, Debug)]");
let _ = writeln!(src, "pub struct {client}<T> {{");
let _ = writeln!(src, " inner: tonic::client::Grpc<T>,");
let _ = writeln!(src, "}}");
let _ = writeln!(src, "impl<T> {client}<T>");
let _ = writeln!(src, "where");
let _ = writeln!(src, " T: tonic::client::GrpcService<tonic::body::Body>,");
let _ = writeln!(src, " T::Error: Into<tonic::codegen::StdError>,");
let _ = writeln!(
src,
" T::ResponseBody: tonic::codegen::Body<Data = tonic::codegen::Bytes> + Send + 'static,"
);
let _ = writeln!(
src,
" <T::ResponseBody as tonic::codegen::Body>::Error: Into<tonic::codegen::StdError> + Send,"
);
let _ = writeln!(src, "{{");
let _ = writeln!(
src,
" pub fn new(inner: T) -> Self {{ Self {{ inner: tonic::client::Grpc::new(inner) }} }}"
);
let _ = writeln!(
src,
" pub fn with_interceptor<F>(inner: T, interceptor: F) -> {client}<tonic::service::interceptor::InterceptedService<T, F>>"
);
let _ = writeln!(src, " where");
let _ = writeln!(src, " F: tonic::service::Interceptor,");
let _ = writeln!(src, " T::ResponseBody: Default,");
let _ = writeln!(src, " T: tonic::codegen::Service<http::Request<tonic::body::Body>, Response = http::Response<<T as tonic::client::GrpcService<tonic::body::Body>>::ResponseBody>>,");
let _ = writeln!(
src,
" <T as tonic::codegen::Service<http::Request<tonic::body::Body>>>::Error: Into<tonic::codegen::StdError> + Send + Sync,"
);
let _ = writeln!(src, " {{");
let _ = writeln!(
src,
" {client}::new(tonic::service::interceptor::InterceptedService::new(inner, interceptor))"
);
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" pub fn send_compressed(mut self, encoding: tonic::codec::CompressionEncoding) -> Self {{ self.inner = self.inner.send_compressed(encoding); self }}"
);
let _ = writeln!(
src,
" pub fn accept_compressed(mut self, encoding: tonic::codec::CompressionEncoding) -> Self {{ self.inner = self.inner.accept_compressed(encoding); self }}"
);
let _ = writeln!(
src,
" pub fn max_decoding_message_size(mut self, limit: usize) -> Self {{ self.inner = self.inner.max_decoding_message_size(limit); self }}"
);
let _ = writeln!(
src,
" pub fn max_encoding_message_size(mut self, limit: usize) -> Self {{ self.inner = self.inner.max_encoding_message_size(limit); self }}"
);
for m in &svc.methods {
emit_client_method(src, m, &path_prefix);
}
let _ = writeln!(src, "}}");
let _ = writeln!(src, "pub struct {server}<T> {{");
let _ = writeln!(src, " inner: Arc<T>,");
let _ = writeln!(
src,
" accept_compression_encodings: tonic::codec::EnabledCompressionEncodings,"
);
let _ = writeln!(
src,
" send_compression_encodings: tonic::codec::EnabledCompressionEncodings,"
);
let _ = writeln!(src, " max_decoding_message_size: Option<usize>,");
let _ = writeln!(src, " max_encoding_message_size: Option<usize>,");
let _ = writeln!(src, "}}");
let _ = writeln!(src, "impl<T> Clone for {server}<T> {{");
let _ = writeln!(src, " fn clone(&self) -> Self {{");
let _ = writeln!(src, " Self {{");
let _ = writeln!(src, " inner: self.inner.clone(),");
let _ = writeln!(
src,
" accept_compression_encodings: self.accept_compression_encodings,"
);
let _ = writeln!(
src,
" send_compression_encodings: self.send_compression_encodings,"
);
let _ = writeln!(
src,
" max_decoding_message_size: self.max_decoding_message_size,"
);
let _ = writeln!(
src,
" max_encoding_message_size: self.max_encoding_message_size,"
);
let _ = writeln!(src, " }}");
let _ = writeln!(src, " }}");
let _ = writeln!(src, "}}");
let _ = writeln!(src, "impl<T: {svc_ty}> {server}<T> {{");
let _ = writeln!(src, " pub fn new(inner: T) -> Self {{");
let _ = writeln!(src, " Self {{");
let _ = writeln!(src, " inner: Arc::new(inner),");
let _ = writeln!(
src,
" accept_compression_encodings: Default::default(),"
);
let _ = writeln!(
src,
" send_compression_encodings: Default::default(),"
);
let _ = writeln!(src, " max_decoding_message_size: None,");
let _ = writeln!(src, " max_encoding_message_size: None,");
let _ = writeln!(src, " }}");
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" pub fn with_interceptor<F>(inner: T, interceptor: F) -> tonic::service::interceptor::InterceptedService<Self, F>"
);
let _ = writeln!(src, " where F: tonic::service::Interceptor {{");
let _ = writeln!(
src,
" tonic::service::interceptor::InterceptedService::new(Self::new(inner), interceptor)"
);
let _ = writeln!(src, " }}");
let _ = writeln!(
src,
" pub fn accept_compressed(mut self, encoding: tonic::codec::CompressionEncoding) -> Self {{ self.accept_compression_encodings.enable(encoding); self }}"
);
let _ = writeln!(
src,
" pub fn send_compressed(mut self, encoding: tonic::codec::CompressionEncoding) -> Self {{ self.send_compression_encodings.enable(encoding); self }}"
);
let _ = writeln!(
src,
" pub fn max_decoding_message_size(mut self, limit: usize) -> Self {{ self.max_decoding_message_size = Some(limit); self }}"
);
let _ = writeln!(
src,
" pub fn max_encoding_message_size(mut self, limit: usize) -> Self {{ self.max_encoding_message_size = Some(limit); self }}"
);
let _ = writeln!(src, "}}");
let _ = writeln!(
src,
"impl<T: {svc_ty}> tonic::server::NamedService for {server}<T> {{"
);
let _ = writeln!(src, " const NAME: &'static str = \"{}\";", svc.full_name);
let _ = writeln!(src, "}}");
let _ = writeln!(
src,
"impl<T, B> tonic::codegen::Service<http::Request<B>> for {server}<T>"
);
let _ = writeln!(src, "where");
let _ = writeln!(src, " T: {svc_ty},");
let _ = writeln!(src, " B: tonic::codegen::Body + Send + 'static,");
let _ = writeln!(
src,
" B::Error: Into<tonic::codegen::StdError> + Send + 'static,"
);
let _ = writeln!(src, "{{");
let _ = writeln!(
src,
" type Response = http::Response<tonic::body::Body>;"
);
let _ = writeln!(src, " type Error = Infallible;");
let _ = writeln!(src, " type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;");
let _ = writeln!(src, " fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {{ Poll::Ready(Ok(())) }}");
let _ = writeln!(
src,
" fn call(&mut self, req: http::Request<B>) -> Self::Future {{"
);
let _ = writeln!(src, " let inner = self.inner.clone();");
let _ = writeln!(
src,
" let accept = self.accept_compression_encodings;"
);
let _ = writeln!(src, " let send = self.send_compression_encodings;");
let _ = writeln!(src, " let max_dec = self.max_decoding_message_size;");
let _ = writeln!(src, " let max_enc = self.max_encoding_message_size;");
let _ = writeln!(src, " Box::pin(async move {{");
let _ = writeln!(src, " match req.uri().path() {{");
for m in &svc.methods {
emit_server_route(src, m, &path_prefix, &svc_ty);
}
let _ = writeln!(src, " _ => {{");
let _ = writeln!(
src,
" let mut response = http::Response::new(tonic::body::Body::default());"
);
let _ = writeln!(
src,
" let headers = response.headers_mut();"
);
let _ = writeln!(src, " headers.insert(tonic::Status::GRPC_STATUS, (tonic::Code::Unimplemented as i32).into());");
let _ = writeln!(src, " headers.insert(http::header::CONTENT_TYPE, tonic::metadata::GRPC_CONTENT_TYPE);");
let _ = writeln!(src, " Ok(response)");
let _ = writeln!(src, " }}");
let _ = writeln!(src, " }}");
let _ = writeln!(src, " }})");
let _ = writeln!(src, " }}");
let _ = writeln!(src, "}}");
}
fn emit_service_trait_method(src: &mut String, m: &MethodDescriptor) {
let fn_name = to_snake(&m.name);
let req = rust_ident(&m.input_type);
let resp = rust_ident(&m.output_type);
let assoc = format!("{}Stream", m.name);
match (m.client_streaming, m.server_streaming) {
(false, false) => {
let _ = writeln!(src, " fn {fn_name}(&self, request: tonic::Request<{req}>) -> impl Future<Output = Result<tonic::Response<{resp}>, tonic::Status>> + Send;");
}
(true, true) => {
let _ = writeln!(src, " type {assoc}: tokio_stream::Stream<Item = Result<{resp}, tonic::Status>> + Send + 'static;");
let _ = writeln!(src, " fn {fn_name}(&self, request: tonic::Request<tonic::Streaming<{req}>>) -> impl Future<Output = Result<tonic::Response<Self::{assoc}>, tonic::Status>> + Send;");
}
(true, false) => {
let _ = writeln!(src, " fn {fn_name}(&self, request: tonic::Request<tonic::Streaming<{req}>>) -> impl Future<Output = Result<tonic::Response<{resp}>, tonic::Status>> + Send;");
}
(false, true) => {
let _ = writeln!(src, " type {assoc}: tokio_stream::Stream<Item = Result<{resp}, tonic::Status>> + Send + 'static;");
let _ = writeln!(src, " fn {fn_name}(&self, request: tonic::Request<{req}>) -> impl Future<Output = Result<tonic::Response<Self::{assoc}>, tonic::Status>> + Send;");
}
}
}
fn emit_client_method(src: &mut String, m: &MethodDescriptor, prefix: &str) {
let fn_name = to_snake(&m.name);
let req = rust_ident(&m.input_type);
let resp = rust_ident(&m.output_type);
let path = format!("{prefix}/{}", m.name);
match (m.client_streaming, m.server_streaming) {
(false, false) => {
let _ = writeln!(src, " pub async fn {fn_name}(&mut self, request: tonic::Request<{req}>) -> Result<tonic::Response<{resp}>, tonic::Status> {{");
let _ = writeln!(src, " self.inner.ready().await.map_err(|e| tonic::Status::unknown(e.into().to_string()))?;");
let _ = writeln!(src, " self.inner.unary(request, \"{path}\".parse().unwrap(), ProtobufCodec::<{req}, {resp}>::default()).await");
let _ = writeln!(src, " }}");
}
(true, true) => {
let _ = writeln!(src, " pub async fn {fn_name}<S>(&mut self, request: tonic::Request<S>) -> Result<tonic::Response<tonic::Streaming<{resp}>>, tonic::Status>");
let _ = writeln!(
src,
" where S: tokio_stream::Stream<Item = {req}> + Send + 'static {{"
);
let _ = writeln!(src, " self.inner.ready().await.map_err(|e| tonic::Status::unknown(e.into().to_string()))?;");
let _ = writeln!(src, " self.inner.streaming(request, \"{path}\".parse().unwrap(), ProtobufCodec::<{req}, {resp}>::default()).await");
let _ = writeln!(src, " }}");
}
(true, false) => {
let _ = writeln!(src, " pub async fn {fn_name}<S>(&mut self, request: tonic::Request<S>) -> Result<tonic::Response<{resp}>, tonic::Status>");
let _ = writeln!(
src,
" where S: tokio_stream::Stream<Item = {req}> + Send + 'static {{"
);
let _ = writeln!(src, " self.inner.ready().await.map_err(|e| tonic::Status::unknown(e.into().to_string()))?;");
let _ = writeln!(src, " self.inner.client_streaming(request, \"{path}\".parse().unwrap(), ProtobufCodec::<{req}, {resp}>::default()).await");
let _ = writeln!(src, " }}");
}
(false, true) => {
let _ = writeln!(src, " pub async fn {fn_name}(&mut self, request: tonic::Request<{req}>) -> Result<tonic::Response<tonic::Streaming<{resp}>>, tonic::Status> {{");
let _ = writeln!(src, " self.inner.ready().await.map_err(|e| tonic::Status::unknown(e.into().to_string()))?;");
let _ = writeln!(src, " self.inner.server_streaming(request, \"{path}\".parse().unwrap(), ProtobufCodec::<{req}, {resp}>::default()).await");
let _ = writeln!(src, " }}");
}
}
}
fn emit_server_route(src: &mut String, m: &MethodDescriptor, prefix: &str, svc_ty: &str) {
let fn_name = to_snake(&m.name);
let req = rust_ident(&m.input_type);
let resp = rust_ident(&m.output_type);
let path = format!("{prefix}/{}", m.name);
match (m.client_streaming, m.server_streaming) {
(false, false) => {
let _ = writeln!(src, " \"{path}\" => {{");
let _ = writeln!(src, " struct Svc<T>(Arc<T>);");
let _ = writeln!(src, " impl<T: {svc_ty}> tonic::server::UnaryService<{req}> for Svc<T> {{");
let _ = writeln!(src, " type Response = {resp};");
let _ = writeln!(src, " type Future = Pin<Box<dyn Future<Output = Result<tonic::Response<{resp}>, tonic::Status>> + Send>>;");
let _ = writeln!(src, " fn call(&mut self, request: tonic::Request<{req}>) -> Self::Future {{");
let _ = writeln!(
src,
" let inner = self.0.clone();"
);
let _ = writeln!(src, " Box::pin(async move {{ inner.{fn_name}(request).await }})");
let _ = writeln!(src, " }}");
let _ = writeln!(src, " }}");
let _ = writeln!(src, " let mut grpc = tonic::server::Grpc::new(ProtobufCodec::<{resp}, {req}>::default()).apply_compression_config(accept, send).apply_max_message_size_config(max_dec, max_enc);");
let _ = writeln!(
src,
" Ok(grpc.unary(Svc(inner), req).await)"
);
let _ = writeln!(src, " }}");
}
(true, true) => {
let assoc = format!("{}Stream", m.name);
let _ = writeln!(src, " \"{path}\" => {{");
let _ = writeln!(src, " struct Svc<T>(Arc<T>);");
let _ = writeln!(src, " impl<T: {svc_ty}> tonic::server::StreamingService<{req}> for Svc<T> {{");
let _ = writeln!(src, " type Response = {resp};");
let _ = writeln!(
src,
" type ResponseStream = T::{assoc};"
);
let _ = writeln!(src, " type Future = Pin<Box<dyn Future<Output = Result<tonic::Response<Self::ResponseStream>, tonic::Status>> + Send>>;");
let _ = writeln!(src, " fn call(&mut self, request: tonic::Request<tonic::Streaming<{req}>>) -> Self::Future {{");
let _ = writeln!(
src,
" let inner = self.0.clone();"
);
let _ = writeln!(src, " Box::pin(async move {{ inner.{fn_name}(request).await }})");
let _ = writeln!(src, " }}");
let _ = writeln!(src, " }}");
let _ = writeln!(src, " let mut grpc = tonic::server::Grpc::new(ProtobufCodec::<{resp}, {req}>::default()).apply_compression_config(accept, send).apply_max_message_size_config(max_dec, max_enc);");
let _ = writeln!(
src,
" Ok(grpc.streaming(Svc(inner), req).await)"
);
let _ = writeln!(src, " }}");
}
(true, false) => {
let _ = writeln!(src, " \"{path}\" => {{");
let _ = writeln!(src, " struct Svc<T>(Arc<T>);");
let _ = writeln!(src, " impl<T: {svc_ty}> tonic::server::ClientStreamingService<{req}> for Svc<T> {{");
let _ = writeln!(src, " type Response = {resp};");
let _ = writeln!(src, " type Future = Pin<Box<dyn Future<Output = Result<tonic::Response<{resp}>, tonic::Status>> + Send>>;");
let _ = writeln!(src, " fn call(&mut self, request: tonic::Request<tonic::Streaming<{req}>>) -> Self::Future {{");
let _ = writeln!(
src,
" let inner = self.0.clone();"
);
let _ = writeln!(src, " Box::pin(async move {{ inner.{fn_name}(request).await }})");
let _ = writeln!(src, " }}");
let _ = writeln!(src, " }}");
let _ = writeln!(src, " let mut grpc = tonic::server::Grpc::new(ProtobufCodec::<{resp}, {req}>::default()).apply_compression_config(accept, send).apply_max_message_size_config(max_dec, max_enc);");
let _ = writeln!(
src,
" Ok(grpc.client_streaming(Svc(inner), req).await)"
);
let _ = writeln!(src, " }}");
}
(false, true) => {
let assoc = format!("{}Stream", m.name);
let _ = writeln!(src, " \"{path}\" => {{");
let _ = writeln!(src, " struct Svc<T>(Arc<T>);");
let _ = writeln!(src, " impl<T: {svc_ty}> tonic::server::ServerStreamingService<{req}> for Svc<T> {{");
let _ = writeln!(src, " type Response = {resp};");
let _ = writeln!(
src,
" type ResponseStream = T::{assoc};"
);
let _ = writeln!(src, " type Future = Pin<Box<dyn Future<Output = Result<tonic::Response<Self::ResponseStream>, tonic::Status>> + Send>>;");
let _ = writeln!(src, " fn call(&mut self, request: tonic::Request<{req}>) -> Self::Future {{");
let _ = writeln!(
src,
" let inner = self.0.clone();"
);
let _ = writeln!(src, " Box::pin(async move {{ inner.{fn_name}(request).await }})");
let _ = writeln!(src, " }}");
let _ = writeln!(src, " }}");
let _ = writeln!(src, " let mut grpc = tonic::server::Grpc::new(ProtobufCodec::<{resp}, {req}>::default()).apply_compression_config(accept, send).apply_max_message_size_config(max_dec, max_enc);");
let _ = writeln!(
src,
" Ok(grpc.server_streaming(Svc(inner), req).await)"
);
let _ = writeln!(src, " }}");
}
}
}