bson-comp
Stop fighting the doc! macro. Make your types generic-compatible.
If you work with MongoDB in Rust, you’ve definitely hit this wall. You have a nice, strongly-typed Enum or Struct, and you just want to use it in a query.
The Problem
You try to do this:
let filter = doc! { "role": Role::Admin };And the compiler screams at you with Error E0277:
the trait bound Bson: std::convert::From<Role> is not satisfiedrequired for Role to implement Into<Bson>So you end up writing this boilerplate everywhere:
// ❌ The Ugly Way// Manual conversion every time you use it.let filter = doc! { "role": bson::to_bson(&Role::Admin).unwrap()};The Solution
bson_comp (BSON Compatibility) handles that boilerplate for you. It implements Into<Bson> for your types so they fit right into the doc! macro.
// ✅ The Clean Waylet filter = doc! { "role": Role::Admin };Installation
Add this to your Cargo.toml:
[dependencies]bson_comp = "0.1"serde = { version = "1.0", features = ["derive"] }How to use it
1. For Enums (The most common use case)
Perfect for status flags, user roles, or category types.
use serde::{Serialize, Deserialize};use bson_comp::BsonComp;use bson::doc;
#[derive(Serialize, Deserialize, BsonComp)]#[serde(rename_all = "UPPERCASE")] // Optional: controls how it looks in Mongoenum Role { Admin, User,}
fn main() { // Works natively in doc! let query = doc! { "role": Role::Admin, "is_active": true };
// Output: { "role": "ADMIN", "is_active": true }}2. For Structs
Useful when embedding objects inside other documents.
use serde::Serialize;use bson_comp::BsonComp;use bson::doc;
#[derive(Serialize, BsonComp)]struct Address { city: String, zip: u32,}
fn main() { let my_address = Address { city: "New York".to_string(), zip: 10001 };
// Embed it directly let profile = doc! { "username": "alice", "address": my_address };}How it works
Rust’s “Orphan Rules” prevent you from implementing From<YourType> for Bson because you don’t own the bson crate.
However, the doc! macro accepts anything that implements Into<Bson>. Since Into is a generic trait, you are allowed to implement it for your own types.
This macro simply generates:
impl Into<bson::Bson> for YourType { fn into(self) -> bson::Bson { bson::to_bson(&self).expect("Failed to serialize type for BSON macro") }}Requirements
- Your type must derive
serde::Serialize. - It panics if serialization fails (which shouldn’t happen for standard Enums/Structs unless you have custom serialization logic that fails).
License
MIT. Enjoy clean code.