Skip to content

RUST-1992 Driver changes to track bson serde-optional API changes #1401

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jun 23, 2025
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -134,11 +134,12 @@ branch = "main"
package = "bson"
version = "3.0.0"
optional = true
features = ["serde"]

[dependencies.mongocrypt]
git = "https://github.com/mongodb/libmongocrypt-rust.git"
branch = "main"
version = "0.3.0"
git = "https://github.com/abr-egn/libmongocrypt-rust.git"
branch = "RUST-1992/bson-optional-serde"
version = "0.3.1"
default-features = false
optional = true

Expand Down
3 changes: 2 additions & 1 deletion src/action/find_and_modify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ impl<T: Serialize + DeserializeOwned + Send + Sync> Collection<T> {
FindOneAndReplace {
coll: self,
filter,
replacement: crate::bson::to_raw_document_buf(replacement.borrow()).map_err(Into::into),
replacement: crate::bson_compat::serialize_to_raw_document_buf(replacement.borrow())
.map_err(Into::into),
options: None,
session: None,
}
Expand Down
5 changes: 4 additions & 1 deletion src/action/insert_many.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ impl<T: Serialize + Send + Sync> Collection<T> {
coll: CollRef::new(self),
docs: docs
.into_iter()
.map(|v| crate::bson::to_raw_document_buf(v.borrow()).map_err(Into::into))
.map(|v| {
crate::bson_compat::serialize_to_raw_document_buf(v.borrow())
.map_err(Into::into)
})
.collect(),
options: None,
session: None,
Expand Down
3 changes: 2 additions & 1 deletion src/action/insert_one.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ impl<T: Serialize + Send + Sync> Collection<T> {
pub fn insert_one(&self, doc: impl Borrow<T>) -> InsertOne {
InsertOne {
coll: CollRef::new(self),
doc: crate::bson::to_raw_document_buf(doc.borrow()).map_err(Into::into),
doc: crate::bson_compat::serialize_to_raw_document_buf(doc.borrow())
.map_err(Into::into),
options: None,
session: None,
}
Expand Down
2 changes: 1 addition & 1 deletion src/action/list_databases.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ impl<'a> Action for ListDatabases<'a, ListSpecifications> {
.and_then(|dbs| {
dbs.into_iter()
.map(|db_spec| {
crate::bson::from_slice(db_spec.as_bytes())
crate::bson_compat::deserialize_from_slice(db_spec.as_bytes())
.map_err(crate::error::Error::from)
})
.collect()
Expand Down
3 changes: 2 additions & 1 deletion src/action/replace_one.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ impl<T: Serialize + Send + Sync> Collection<T> {
ReplaceOne {
coll: CollRef::new(self),
query,
replacement: crate::bson::to_raw_document_buf(replacement.borrow()).map_err(Into::into),
replacement: crate::bson_compat::serialize_to_raw_document_buf(replacement.borrow())
.map_err(Into::into),
options: None,
session: None,
}
Expand Down
122 changes: 111 additions & 11 deletions src/bson_compat.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,131 @@
#[cfg(feature = "bson-3")]
pub(crate) trait RawDocumentBufExt {
fn append_ref<'a>(
use crate::bson::RawBson;

pub(crate) trait RawDocumentBufExt: Sized {
fn append_err(&mut self, key: impl AsRef<str>, value: impl Into<RawBson>) -> RawResult<()>;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since append and similar are now fallible in 3.0, the compatibility layer needed to make them fallible (at least at the type level) for 2.x as well, even if they never actually fail. The other option would be to wrap the 3.x variants in expect but this way people opting in to bson-3 get nicer error behavior as a bonus :)


fn append_ref_err<'a>(
&mut self,
key: impl AsRef<str>,
value: impl Into<crate::bson::raw::RawBsonRef<'a>>,
);
) -> RawResult<()>;

#[cfg(not(feature = "bson-3"))]
fn decode_from_bytes(data: Vec<u8>) -> RawResult<Self>;
}

#[cfg(feature = "bson-3")]
impl RawDocumentBufExt for crate::bson::RawDocumentBuf {
fn append_ref<'a>(
fn append_err(&mut self, key: impl AsRef<str>, value: impl Into<RawBson>) -> RawResult<()> {
self.append(key, value.into())
}

fn append_ref_err<'a>(
&mut self,
key: impl AsRef<str>,
value: impl Into<crate::bson::raw::RawBsonRef<'a>>,
) {
) -> RawResult<()> {
self.append(key, value)
}
}

#[cfg(not(feature = "bson-3"))]
impl RawDocumentBufExt for crate::bson::RawDocumentBuf {
fn append_err(&mut self, key: impl AsRef<str>, value: impl Into<RawBson>) -> RawResult<()> {
self.append(key, value);
Ok(())
}

fn append_ref_err<'a>(
&mut self,
key: impl AsRef<str>,
value: impl Into<crate::bson::raw::RawBsonRef<'a>>,
) -> RawResult<()> {
self.append_ref(key, value);
Ok(())
}

fn decode_from_bytes(data: Vec<u8>) -> RawResult<Self> {
Self::from_bytes(data)
}
}

pub(crate) trait RawArrayBufExt: Sized {
#[allow(dead_code)]
fn from_iter_err<V: Into<RawBson>, I: IntoIterator<Item = V>>(iter: I) -> RawResult<Self>;

fn push_err(&mut self, value: impl Into<RawBson>) -> RawResult<()>;
}

#[cfg(feature = "bson-3")]
pub(crate) use crate::bson::error::Result as RawResult;
impl RawArrayBufExt for crate::bson::RawArrayBuf {
fn from_iter_err<V: Into<RawBson>, I: IntoIterator<Item = V>>(iter: I) -> RawResult<Self> {
Self::from_iter(iter.into_iter().map(|v| v.into()))
}

fn push_err(&mut self, value: impl Into<RawBson>) -> RawResult<()> {
self.push(value.into())
}
}

#[cfg(not(feature = "bson-3"))]
pub(crate) use crate::bson::raw::Result as RawResult;
impl RawArrayBufExt for crate::bson::RawArrayBuf {
fn from_iter_err<V: Into<RawBson>, I: IntoIterator<Item = V>>(iter: I) -> RawResult<Self> {
Ok(Self::from_iter(iter))
}

#[cfg(feature = "bson-3")]
pub(crate) use crate::bson::error::Error as RawError;
fn push_err(&mut self, value: impl Into<RawBson>) -> RawResult<()> {
Ok(self.push(value))
}
}

#[cfg(not(feature = "bson-3"))]
pub(crate) use crate::bson::raw::Error as RawError;
pub(crate) trait RawDocumentExt {
fn decode_from_bytes<D: AsRef<[u8]> + ?Sized>(data: &D) -> RawResult<&Self>;
}

#[cfg(not(feature = "bson-3"))]
impl RawDocumentExt for crate::bson::RawDocument {
fn decode_from_bytes<D: AsRef<[u8]> + ?Sized>(data: &D) -> RawResult<&Self> {
Self::from_bytes(data)
}
}

#[cfg(not(feature = "bson-3"))]
#[allow(dead_code)]
pub(crate) trait DocumentExt {
fn encode_to_vec(&self) -> crate::bson::ser::Result<Vec<u8>>;
}

#[cfg(not(feature = "bson-3"))]
impl DocumentExt for crate::bson::Document {
fn encode_to_vec(&self) -> crate::bson::ser::Result<Vec<u8>> {
let mut out = vec![];
self.to_writer(&mut out)?;
Ok(out)
}
}

macro_rules! use_either {
Copy link
Contributor Author

@abr-egn abr-egn Jun 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is kind of a frivolous use of a macro but I got real tired of writing these out by hand and I find the tabular format a lot easier to read. I can revert if you feel strongly :)

($($name:ident => $path3:path | $path2:path);+;) => {
$(
#[cfg(feature = "bson-3")]
pub(crate) use crate::bson::{$path3 as $name};

#[cfg(not(feature = "bson-3"))]
#[allow(unused_imports)]
pub(crate) use crate::bson::{$path2 as $name};
)+
};
}

// Exported name => bson3 import | bson2 import
use_either! {
RawResult => error::Result | raw::Result;
RawError => error::Error | raw::Error;
serialize_to_raw_document_buf => serialize_to_raw_document_buf | to_raw_document_buf;
serialize_to_document => serialize_to_document | to_document;
serialize_to_bson => serialize_to_bson | to_bson;
deserialize_from_slice => deserialize_from_slice | from_slice;
deserialize_from_document => deserialize_from_document | from_document;
deserialize_from_bson => deserialize_from_bson | from_bson;
}
24 changes: 11 additions & 13 deletions src/bson_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,12 @@ use crate::{
RawBsonRef,
RawDocumentBuf,
},
bson_compat::{RawArrayBufExt, RawDocumentBufExt as _},
checked::Checked,
error::{Error, ErrorKind, Result},
runtime::SyncLittleEndianRead,
};

#[cfg(feature = "bson-3")]
use crate::bson_compat::RawDocumentBufExt as _;

/// Coerce numeric types into an `i64` if it would be lossless to do so. If this Bson is not numeric
/// or the conversion would be lossy (e.g. 1.5 -> 1), this returns `None`.
#[allow(clippy::cast_possible_truncation)]
Expand Down Expand Up @@ -80,14 +78,14 @@ pub(crate) fn to_bson_array(docs: &[Document]) -> Bson {
pub(crate) fn to_raw_bson_array(docs: &[Document]) -> Result<RawBson> {
let mut array = RawArrayBuf::new();
for doc in docs {
array.push(RawDocumentBuf::from_document(doc)?);
array.push_err(RawDocumentBuf::from_document(doc)?)?;
}
Ok(RawBson::Array(array))
}
pub(crate) fn to_raw_bson_array_ser<T: Serialize>(values: &[T]) -> Result<RawBson> {
let mut array = RawArrayBuf::new();
for value in values {
array.push(crate::bson::to_raw_document_buf(value)?);
array.push_err(crate::bson_compat::serialize_to_raw_document_buf(value)?)?;
}
Ok(RawBson::Array(array))
}
Expand Down Expand Up @@ -149,12 +147,12 @@ pub(crate) fn array_entry_size_bytes(index: usize, doc_len: usize) -> Result<usi
(Checked::new(1) + num_decimal_digits(index) + 1 + doc_len).get()
}

pub(crate) fn vec_to_raw_array_buf(docs: Vec<RawDocumentBuf>) -> RawArrayBuf {
pub(crate) fn vec_to_raw_array_buf(docs: Vec<RawDocumentBuf>) -> Result<RawArrayBuf> {
let mut array = RawArrayBuf::new();
for doc in docs {
array.push(doc);
array.push_err(doc)?;
}
array
Ok(array)
}

/// The number of digits in `n` in base 10.
Expand Down Expand Up @@ -202,7 +200,7 @@ pub(crate) fn extend_raw_document_buf(
k
)));
}
this.append(k, v.to_raw_bson());
this.append_err(k, v.to_raw_bson())?;
}
Ok(())
}
Expand All @@ -216,13 +214,13 @@ pub(crate) fn append_ser(
struct Helper<T> {
value: T,
}
let raw_doc = crate::bson::to_raw_document_buf(&Helper { value })?;
this.append_ref(
let raw_doc = crate::bson_compat::serialize_to_raw_document_buf(&Helper { value })?;
this.append_ref_err(
key,
raw_doc
.get("value")?
.ok_or_else(|| Error::internal("no value"))?,
);
)?;
Ok(())
}

Expand All @@ -243,7 +241,7 @@ pub(crate) fn get_or_prepend_id_field(doc: &mut RawDocumentBuf) -> Result<Bson>
let new_length: i32 = Checked::new(new_bytes.len()).try_into()?;
new_bytes[0..4].copy_from_slice(&new_length.to_le_bytes());

*doc = RawDocumentBuf::from_bytes(new_bytes)?;
*doc = RawDocumentBuf::decode_from_bytes(new_bytes)?;

Ok(id.into())
}
Expand Down
4 changes: 3 additions & 1 deletion src/change_stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,9 @@ where
/// ```
pub async fn next_if_any(&mut self) -> Result<Option<T>> {
Ok(match NextInBatchFuture::new(self).await? {
BatchValue::Some { doc, .. } => Some(crate::bson::from_slice(doc.as_bytes())?),
BatchValue::Some { doc, .. } => {
Some(crate::bson_compat::deserialize_from_slice(doc.as_bytes())?)
}
BatchValue::Empty | BatchValue::Exhausted => None,
})
}
Expand Down
4 changes: 3 additions & 1 deletion src/change_stream/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,9 @@ where
match bv {
BatchValue::Some { doc, .. } => {
self.data.document_returned = true;
return Ok(Some(crate::bson::from_slice(doc.as_bytes())?));
return Ok(Some(crate::bson_compat::deserialize_from_slice(
doc.as_bytes(),
)?));
}
BatchValue::Empty | BatchValue::Exhausted => return Ok(None),
}
Expand Down
Loading