Understanding generated code structure and helper functions from bomboni_prost
When you use bomboni_prost to compile Protocol Buffers, it generates additional helper code beyond the standard prost output. This page explains the structure and features of the generated code.
Generated code is written to files with a .plus suffix to distinguish it from base prost output:
// Include both base and enhanced codebomboni_proto::include_proto!("bookstore.v1"); // from prost-buildbomboni_proto::include_proto!("bookstore.v1.plus"); // from bomboni_prost
The .plus suffix makes it clear which code comes from Bomboni and allows you to use both codebases side-by-side.
use bomboni_proto::google::rpc::RetryInfo;// Use in error messagesfn validate(retry_info: &RetryInfo) -> Result<(), String> { if retry_info.retry_delay.is_none() { return Err(format!( "Field {} is required", RetryInfo::RETRY_DELAY_FIELD_NAME )); } Ok(())}
use bomboni_proto::google::rpc::Code;// Get all possible valuesfor name in Code::VALUE_NAMES { println!("Code value: {}", name);}// Use in validationfn is_valid_code_name(name: &str) -> bool { Code::VALUE_NAMES.contains(&name)}// Metadata for loggingprintln!("Enum: {}.{}", Code::PACKAGE, Code::NAME);
impl ::serde::Serialize for Code { fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: ::serde::Serializer, { // Serializes as string name like "INVALID_ARGUMENT" }}impl<'de> ::serde::Deserialize<'de> for Code { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: ::serde::Deserializer<'de>, { // Deserializes from string name }}
When api.helpers_mod is set, Bomboni generates helper modules for working with enum fields:
pub mod helpers { /// Utility for working with i32s in message fields. /// Usable with #[serde(with = "...")] pub mod code_serde { use ::serde::{Serialize, Deserialize}; pub fn serialize<S>( value: &i32, serializer: S, ) -> Result<S::Ok, S::Error> where S: ::serde::Serializer, { // Serializes i32 as enum name } pub fn deserialize<'de, D>(deserializer: D) -> Result<i32, D::Error> where D: ::serde::Deserializer<'de>, { // Deserializes enum name to i32 } }}
Use the generated helpers with serde field attributes:
use serde::{Serialize, Deserialize};#[derive(Serialize, Deserialize)]struct MyMessage { // Use helper to serialize i32 field as enum name #[serde(with = "helpers::code_serde")] code: i32,}
Bomboni generates From implementations for easy construction:
// From variant to messageimpl From<value::Kind> for Value { fn from(value: value::Kind) -> Self { Self { kind: Some(value) } }}// From inner type to variantimpl From<String> for value::Kind { fn from(value: String) -> Self { Self::StringValue(value) }}// From inner type directly to messageimpl From<String> for Value { fn from(value: String) -> Self { Self { kind: Some(value.into()) } }}
use bomboni_proto::google::protobuf::Value;// Create using Fromlet value = Value::from("hello".to_string());let value = Value::from(42.0);let value = Value::from(true);// Get variant nameif let Some(kind) = &value.kind { println!("Variant: {}", kind.get_variant_name());}// Pattern matching with constantsmatch &value.kind { Some(kind) if kind.get_variant_name() == value::Kind::STRING_VALUE_VARIANT_NAME => { println!("It's a string"); } Some(kind) if kind.get_variant_name() == value::Kind::NUMBER_VALUE_VARIANT_NAME => { println!("It's a number"); } _ => {}}