Skip to main content
The bomboni_proto crate provides enhanced implementations of Google’s well-known Protocol Buffer types with additional functionality beyond the standard prost-types.

Installation

Add bomboni_proto to your dependencies:
Cargo.toml
[dependencies]
bomboni_proto = "0.1"

# With tonic integration
bomboni_proto = { version = "0.1", features = ["tonic"] }

Any Type

The Any type allows you to pack arbitrary protobuf messages with automatic type URL handling.

Converting to Any

Use from_msg to convert any protobuf message to an Any type:
use bomboni_proto::google::protobuf::Any;
use bomboni_proto::google::rpc::ErrorInfo;

let error_info = ErrorInfo {
    reason: "INVALID_ARGUMENT".to_string(),
    domain: "my.api".to_string(),
    metadata: Default::default(),
};

// Convert to Any with automatic type URL
let any_msg = Any::from_msg(&error_info).unwrap();

assert_eq!(any_msg.type_url, "type.googleapis.com/google.rpc.ErrorInfo");

Converting from Any

Use to_msg to unpack an Any message back to its original type:
use bomboni_proto::google::protobuf::Any;
use bomboni_proto::google::rpc::ErrorInfo;

let any_msg = Any::from_msg(&ErrorInfo {
    reason: "test".to_string(),
    domain: "example".to_string(),
    metadata: Default::default(),
}).unwrap();

// Convert back to original type
let decoded: ErrorInfo = any_msg.to_msg().unwrap();
assert_eq!(decoded.reason, "test");
to_msg returns a DecodeError if the type URL doesn’t match the expected type or if decoding fails.

TryFrom Conversions

For convenience, you can implement automatic TryFrom conversions:
use bomboni_proto::google::protobuf::Any;
use bomboni_proto::google::rpc::ErrorInfo;
use bomboni_proto::impl_proto_any_convert;

// Implement bidirectional conversions
impl_proto_any_convert![ErrorInfo];

// Now you can use TryFrom
let error_info = ErrorInfo::default();
let any_msg: Any = error_info.try_into().unwrap();
let decoded: ErrorInfo = any_msg.try_into().unwrap();

RPC Status and Codes

The Status type provides comprehensive error handling with support for detailed error information.

Creating Status Messages

use bomboni_proto::google::rpc::{Status, Code, ErrorInfo, BadRequest, bad_request::FieldViolation};
use bomboni_proto::google::protobuf::Any;

// Create a status with error details
let bad_request = BadRequest {
    field_violations: vec![
        FieldViolation {
            field: "name".to_string(),
            description: "Required field".to_string(),
        },
        FieldViolation {
            field: "email".to_string(),
            description: "Invalid email format".to_string(),
        },
    ],
};

let status = Status::new(
    Code::InvalidArgument,
    "Invalid request parameters".to_string(),
    vec![Any::from_msg(&bad_request).unwrap()],
);

Status Codes

The Code enum provides all standard gRPC status codes:
use bomboni_proto::google::rpc::Code;

match result {
    Ok(_) => Code::Ok,
    Err(e) if e.is_not_found() => Code::NotFound,
    Err(e) if e.is_invalid() => Code::InvalidArgument,
    Err(e) if e.is_unauthorized() => Code::Unauthenticated,
    Err(_) => Code::Internal,
}

Tonic Integration

With the tonic feature enabled, you can convert between Bomboni’s Status and tonic’s Status:
use bomboni_proto::google::rpc::{Status, Code, BadRequest, bad_request::FieldViolation};
use bomboni_proto::google::protobuf::Any;

let bad_request = BadRequest {
    field_violations: vec![
        FieldViolation {
            field: "email".to_string(),
            description: "Required field".to_string(),
        }
    ],
};

let status = Status::new(
    Code::InvalidArgument,
    "Invalid request".to_string(),
    vec![Any::from_msg(&bad_request).unwrap()],
);

// Convert to tonic::Status for gRPC responses
let tonic_status: tonic::Status = status.try_into().unwrap();

// Convert back from tonic::Status
let converted_back: Status = tonic_status.try_into().unwrap();

HTTP Status Code Conversion

With the tonic feature, convert gRPC codes to HTTP status codes:
use bomboni_proto::google::rpc::Code;
use http::StatusCode;

assert_eq!(Code::InvalidArgument.to_status_code(), StatusCode::BAD_REQUEST);
assert_eq!(Code::NotFound.to_status_code(), StatusCode::NOT_FOUND);
assert_eq!(Code::Unauthenticated.to_status_code(), StatusCode::UNAUTHORIZED);
assert_eq!(Code::PermissionDenied.to_status_code(), StatusCode::FORBIDDEN);

Error Details

Bomboni provides rich error detail types that follow Google’s error model:

ErrorInfo

use bomboni_proto::google::rpc::ErrorInfo;
use std::collections::BTreeMap;

let error_info = ErrorInfo {
    reason: "INVALID_ARGUMENT".to_string(),
    domain: "my.api".to_string(),
    metadata: BTreeMap::from([
        ("field".to_string(), "email".to_string()),
        ("constraint".to_string(), "format".to_string()),
    ]),
};

BadRequest

use bomboni_proto::google::rpc::{BadRequest, bad_request::FieldViolation};

let bad_request = BadRequest {
    field_violations: vec![
        FieldViolation {
            field: "user.email".to_string(),
            description: "Must be a valid email address".to_string(),
        },
        FieldViolation {
            field: "user.age".to_string(),
            description: "Must be a positive integer".to_string(),
        },
    ],
};

RetryInfo

use bomboni_proto::google::rpc::RetryInfo;
use bomboni_proto::google::protobuf::Duration;

let retry_info = RetryInfo {
    retry_delay: Some(Duration {
        seconds: 30,
        nanos: 0,
    }),
};

Other Error Types

  • DebugInfo - Debugging information
  • QuotaFailure - Quota violation details
  • PreconditionFailure - Failed precondition details
  • RequestInfo - Request identification
  • ResourceInfo - Resource metadata
  • Help - Links to documentation
  • LocalizedMessage - User-facing error messages

Field Masks

The FieldMask type helps specify which fields to return or update in API operations.

Creating Field Masks

use bomboni_proto::google::protobuf::FieldMask;

let mask = FieldMask {
    paths: vec![
        "user.name".to_string(),
        "user.email".to_string(),
    ],
};

// Or use From trait
let mask = FieldMask::from(vec!["user.name", "user.email"]);

Field Mask Operations

use bomboni_proto::google::protobuf::FieldMask;

let mask = FieldMask::new(vec![
    "user.profile".to_string(),
    "user.settings".to_string(),
]);

// Check if mask contains a path
assert!(mask.contains("user.profile"));
assert!(!mask.contains("user.name"));

// Check if mask covers a nested field
assert!(mask.masks("user.profile.avatar"));  // true - parent is masked
assert!(!mask.masks("user.name"));           // false - not in mask
The masks method checks if a field path is covered by any of the mask’s paths, including parent paths.

Update Operations

Field masks are commonly used in update operations:
use bomboni_proto::google::protobuf::FieldMask;

// Only update specified fields
let update_mask = FieldMask::from(vec!["name", "email"]);

if update_mask.masks("name") {
    user.name = update.name;
}
if update_mask.masks("email") {
    user.email = update.email;
}
// Other fields remain unchanged

Serde Integration

All types include comprehensive serde support:
use bomboni_proto::google::rpc::ErrorInfo;
use std::collections::BTreeMap;

let error_info = ErrorInfo {
    reason: "INVALID_ARGUMENT".to_string(),
    domain: "my.api".to_string(),
    metadata: BTreeMap::default(),
};

// Serialize to JSON
let json = serde_json::to_string(&error_info).unwrap();

// Deserialize from JSON
let parsed: ErrorInfo = serde_json::from_str(&json).unwrap();

assert_eq!(parsed.reason, "INVALID_ARGUMENT");

Status Serialization

use bomboni_proto::google::rpc::{Status, Code, ErrorInfo};
use bomboni_proto::google::protobuf::Any;

let status = Status::new(
    Code::InvalidArgument,
    "error".to_string(),
    vec![
        Any::from_msg(&ErrorInfo {
            reason: "a".to_string(),
            domain: "b".to_string(),
            metadata: Default::default(),
        }).unwrap(),
    ],
);

let json = serde_json::to_string(&status).unwrap();
// Output:
// {"code":"INVALID_ARGUMENT","message":"error","details":[{"@type":"type.googleapis.com/google.rpc.ErrorInfo","reason":"a","domain":"b"}]}

Well-Known Types

Bomboni includes enhanced implementations of all Google well-known types:
  • google.protobuf.Any - Dynamic message type
  • google.protobuf.Timestamp - Point in time
  • google.protobuf.Duration - Time span
  • google.protobuf.Empty - Empty message
  • google.protobuf.FieldMask - Field selection
  • google.protobuf.Struct - Dynamic struct
  • google.protobuf.Value - Dynamic value
  • google.protobuf.*Wrapper - Optional primitive wrappers

Cargo Features

tonic
feature
Enable integration with the tonic gRPC library. Provides conversions between Bomboni’s Status and tonic’s Status, as well as HTTP status code conversions.
chrono
feature
Enable compatibility with the chrono datetime library for Timestamp and Duration types.
wasm
feature
Enable WebAssembly support and JavaScript bindings.
js
feature
Enable JavaScript-specific type mappings for use in WASM environments.
testing
feature
Enable testing utilities for working with protobuf types in tests.

Next Steps

Code Generation

Learn about generated code structure and helpers

Prost Configuration

Configure build-time code generation

Build docs developers (and LLMs) love