Skip to main content
The bomboni_prost crate provides utilities for compiling Protocol Buffers with prost and generating additional helper functions beyond the standard prost output.

Installation

Add bomboni_prost as a build dependency:
Cargo.toml
[build-dependencies]
bomboni_prost = "0.1"

Basic Usage

Use the compile function in your build.rs to generate enhanced Rust code from Protocol Buffers:
build.rs
use bomboni_prost::{compile, config::CompileConfig};

fn main() {
    let config = CompileConfig {
        file_descriptor_set_path: "descriptor.bin".into(),
        output_path: "src/generated".into(),
        format: true,
        ..Default::default()
    };
    
    compile(config).expect("Failed to compile protobuf files");
}
The compile function expects a file descriptor set that has been generated by protoc --descriptor_set_out. This is typically created by tonic-build or prost-build.

CompileConfig

The CompileConfig struct controls all aspects of code generation:
use bomboni_prost::config::{CompileConfig, ApiConfig};
use bomboni_prost::path_map::PathMap;
use std::path::PathBuf;

let config = CompileConfig {
    // Path to the file descriptor set binary
    file_descriptor_set_path: PathBuf::from("descriptor.bin"),
    
    // Output directory for generated code
    output_path: PathBuf::from("src/generated"),
    
    // Whether to format generated code with rustfmt
    format: true,
    
    // API-specific configuration
    api: ApiConfig {
        names: true,          // Generate name constants
        field_names: true,    // Generate field name constants  
        type_url: true,       // Generate type URL functions
        oneof_utility: true,  // Generate oneof helpers
        domain: None,         // Optional domain prefix
        serde: true,          // Generate serde implementations
        helpers_mod: Some("helpers".into()),  // Helpers module name
    },
    
    // External path mappings
    external_paths: PathMap::default(),
};

Configuration Fields

file_descriptor_set_path
PathBuf
required
Path to the file descriptor set binary file generated by protoc --descriptor_set_out
output_path
PathBuf
required
Output directory where generated Rust files will be written
format
bool
default:"true"
Whether to format generated code using rustfmt
api
ApiConfig
Controls generation of API-related helper functions
external_paths
PathMap
Maps protobuf type paths to custom Rust types for external integration

ApiConfig Options

The ApiConfig struct provides fine-grained control over generated helpers:
names
bool
default:"true"
Generate NAME and PACKAGE constants for messages and enums
impl Code {
    pub const NAME: &'static str = "Code";
    pub const PACKAGE: &'static str = "google.rpc";
}
field_names
bool
default:"true"
Generate field name constants for message fields
impl RetryInfo {
    pub const RETRY_DELAY_FIELD_NAME: &'static str = "retry_delay";
}
type_url
bool
default:"true"
Generate type URL functions for use with Any types
impl ErrorInfo {
    pub const TYPE_URL: &'static str = "type.googleapis.com/google.rpc.ErrorInfo";
}
oneof_utility
bool
default:"true"
Generate utility functions for working with oneof fields (see Code Generation)
domain
Option<String>
default:"None"
Optional domain prefix to add to generated type names
serde
bool
default:"true"
Generate Serialize and Deserialize implementations for all types
helpers_mod
Option<String>
default:"None"
Name of module to contain helper functions. If None, helpers are placed inline

Path Mapping

The PathMap type allows you to map protobuf types to custom Rust implementations:
use bomboni_prost::path_map::PathMap;

let mut external_paths = PathMap::default();

// Map google.protobuf.Timestamp to chrono::DateTime
external_paths.insert(
    ".google.protobuf.Timestamp",
    "chrono::DateTime<chrono::Utc>"
);

// Map custom types to your own implementations
external_paths.insert(
    ".myapi.User",
    "crate::models::User"
);

PathMap Methods

insert
fn(&mut self, matcher: S, value: T)
Insert a new matcher-value pair. The map is automatically sorted after insertion.
map.insert(".google.protobuf.Duration", "std::time::Duration");
get_first
fn(&self, path: &str) -> Option<(&str, &str)>
Get the first matching value for a path. Exact matches take precedence over prefix matches.
let (matcher, value) = map.get_first(".myapi.User.name").unwrap();
get_first_field
fn(&self, path: &str, field: &str) -> Option<(&str, &str)>
Get the first matching value for a specific field within a type.
let (matcher, value) = map.get_first_field(".myapi.User", "email").unwrap();

Including Generated Code

After compilation, generated code is written to files with a .plus suffix:
// Include both prost-generated code and bomboni helpers
bomboni_proto::include_proto!("bookstore.v1");      // from prost-build
bomboni_proto::include_proto!("bookstore.v1.plus"); // from bomboni_prost
The .plus suffix distinguishes Bomboni’s enhanced code from the base prost-generated code, allowing you to use both side-by-side.

Integration with tonic-build

Combine bomboni_prost with tonic-build for complete gRPC support:
build.rs
use bomboni_prost::{compile, config::CompileConfig};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Step 1: Use tonic-build to generate base code
    tonic_build::configure()
        .file_descriptor_set_path("target/descriptor.bin")
        .out_dir("src/generated")
        .compile(
            &["proto/bookstore.proto"],
            &["proto"],
        )?;
    
    // Step 2: Use bomboni_prost to generate enhanced helpers
    let config = CompileConfig {
        file_descriptor_set_path: "target/descriptor.bin".into(),
        output_path: "src/generated".into(),
        format: true,
        ..Default::default()
    };
    
    compile(config)?;
    
    Ok(())
}

Complete Example

Here’s a complete example showing all configuration options:
build.rs
use bomboni_prost::{compile, config::{CompileConfig, ApiConfig}, path_map::PathMap};
use std::path::PathBuf;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Configure tonic-build
    tonic_build::configure()
        .file_descriptor_set_path("target/descriptor.bin")
        .out_dir("src/generated")
        .compile(
            &["proto/api.proto"],
            &["proto"],
        )?;
    
    // Configure path mappings
    let mut external_paths = PathMap::default();
    external_paths.insert(".google.protobuf.Timestamp", "::prost_types::Timestamp");
    
    // Configure bomboni_prost
    let config = CompileConfig {
        file_descriptor_set_path: PathBuf::from("target/descriptor.bin"),
        output_path: PathBuf::from("src/generated"),
        format: true,
        api: ApiConfig {
            names: true,
            field_names: true,
            type_url: true,
            oneof_utility: true,
            domain: Some("myapi.com".to_string()),
            serde: true,
            helpers_mod: Some("helpers".to_string()),
        },
        external_paths,
    };
    
    compile(config)?;
    
    Ok(())
}

Next Steps

Code Generation

Learn about generated helper functions and utilities

Runtime Types

Explore enhanced protobuf types for runtime use

Build docs developers (and LLMs) love