Skip to content

RUST-1699: Add serde_with 3.x integration #422

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 16 commits into from
Jul 27, 2023
Merged
92 changes: 82 additions & 10 deletions .evergreen/Cargo.lock.msrv

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

4 changes: 2 additions & 2 deletions .evergreen/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -195,9 +195,9 @@ axes:
- id: "extra-rust-versions"
values:
- id: "min"
display_name: "1.56 (minimum supported version)"
display_name: "1.60 (minimum supported version)"
variables:
RUST_VERSION: "1.56.0"
RUST_VERSION: "1.60.0"
MSRV: "true"
- id: "nightly"
display_name: "nightly"
Expand Down
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,13 @@ lazy_static = "1.4.0"
uuid-0_8 = { package = "uuid", version = "0.8.1", features = ["serde", "v4"], optional = true }
uuid = { version = "1.1.2", features = ["serde", "v4"] }
serde_bytes = "0.11.5"
serde_with = { version = "1", optional = true }
serde_with = { version = "1.3.1", optional = true }
serde_with-3 = { package = "serde_with", version = "3.1.0", optional = true }
time = { version = "0.3.9", features = ["formatting", "parsing", "macros", "large-dates"] }
bitvec = "1.0.1"

[target.'cfg(target_arch = "wasm32")'.dependencies]
js-sys = "0.3"

[dev-dependencies]
assert_matches = "1.2"
criterion = "0.3.0"
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ Note that if you are using `bson` through the `mongodb` crate, you do not need t
| `chrono-0_4` | Enable support for v0.4 of the [`chrono`](https://docs.rs/chrono/0.4) crate in the public API. | n/a | no |
| `uuid-0_8` | Enable support for v0.8 of the [`uuid`](https://docs.rs/uuid/0.8) crate in the public API. | n/a | no |
| `uuid-1` | Enable support for v1.x of the [`uuid`](https://docs.rs/uuid/1.0) crate in the public API. | n/a | no |
| `serde_with` | Enable [`serde_with`](https://docs.rs/serde_with/latest) integrations for `bson::DateTime` and `bson::Uuid` | serde_with | no |

| `serde_with` | Enable [`serde_with`](https://docs.rs/serde_with/1.x) 1.x integrations for `bson::DateTime` and `bson::Uuid`.| serde_with | no |
| `serde_with-3` | Enable [`serde_with`](https://docs.rs/serde_with/3.x) 3.x integrations for `bson::DateTime` and `bson::Uuid`. Requires Rust 1.61. | serde_with | no |
## Overview of the BSON Format

BSON, short for Binary JSON, is a binary-encoded serialization of JSON-like documents.
Expand Down
71 changes: 64 additions & 7 deletions src/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,20 +145,20 @@ use serde_with::{DeserializeAs, SerializeAs};
/// }
/// # }
/// ```
/// ### The `serde_with` feature flag
/// ### The `serde_with-3` feature flag
///
/// The `serde_with` feature can be enabled to support more ergonomic serde attributes for
/// The `serde_with-3` feature can be enabled to support more ergonomic serde attributes for
/// (de)serializing [`chrono::DateTime`] from/to BSON via the [`serde_with`](https://docs.rs/serde_with/1.11.0/serde_with/)
/// crate. The main benefit of this compared to the regular `serde_helpers` is that `serde_with` can
/// handle nested [`chrono::DateTime`] values (e.g. in [`Option`]), whereas the former only works on
/// fields that are exactly [`chrono::DateTime`].
/// crate. The main benefit of this compared to the regular `serde_helpers` is that `serde_with-3`
/// can handle nested [`chrono::DateTime`] values (e.g. in [`Option`]), whereas the former only
/// works on fields that are exactly [`chrono::DateTime`].
/// ```
/// # #[cfg(all(feature = "chrono-0_4", feature = "serde_with"))]
/// # #[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))]
/// # {
/// use serde::{Deserialize, Serialize};
/// use bson::doc;
///
/// #[serde_with::serde_as]
/// #[serde_with_3::serde_as]
/// #[derive(Deserialize, Serialize, PartialEq, Debug)]
/// struct Foo {
/// /// Serializes as a BSON datetime rather than using [`chrono::DateTime`]'s serialization
Expand Down Expand Up @@ -466,6 +466,36 @@ impl SerializeAs<chrono::DateTime<Utc>> for crate::DateTime {
}
}

#[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))]
#[cfg_attr(
docsrs,
doc(cfg(all(feature = "chrono-0_4", feature = "serde_with-3")))
)]
impl<'de> serde_with_3::DeserializeAs<'de, chrono::DateTime<Utc>> for crate::DateTime {
fn deserialize_as<D>(deserializer: D) -> std::result::Result<chrono::DateTime<Utc>, D::Error>
where
D: Deserializer<'de>,
{
let dt = DateTime::deserialize(deserializer)?;
Ok(dt.to_chrono())
}
}

#[cfg(all(feature = "chrono-0_4", feature = "serde_with-3"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "chrono-0_4", feature = "chrono-0_4"))))]
impl serde_with_3::SerializeAs<chrono::DateTime<Utc>> for crate::DateTime {
fn serialize_as<S>(
source: &chrono::DateTime<Utc>,
serializer: S,
) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let dt = DateTime::from_chrono(*source);
dt.serialize(serializer)
}
}

#[cfg(feature = "time-0_3")]
#[cfg_attr(docsrs, doc(cfg(feature = "time-0_3")))]
impl From<crate::DateTime> for time::OffsetDateTime {
Expand Down Expand Up @@ -509,6 +539,33 @@ impl SerializeAs<time::OffsetDateTime> for crate::DateTime {
}
}

#[cfg(all(feature = "time-0_3", feature = "serde_with-3"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "time-0_3", feature = "serde_with-3"))))]
impl<'de> serde_with_3::DeserializeAs<'de, time::OffsetDateTime> for crate::DateTime {
fn deserialize_as<D>(deserializer: D) -> std::result::Result<time::OffsetDateTime, D::Error>
where
D: Deserializer<'de>,
{
let dt = DateTime::deserialize(deserializer)?;
Ok(dt.to_time_0_3())
}
}

#[cfg(all(feature = "time-0_3", feature = "serde_with-3"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "time-0_3", feature = "chrono-0_4"))))]
impl serde_with_3::SerializeAs<time::OffsetDateTime> for crate::DateTime {
fn serialize_as<S>(
source: &time::OffsetDateTime,
serializer: S,
) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let dt = DateTime::from_time_0_3(*source);
dt.serialize(serializer)
}
}

/// Errors that can occur during [`DateTime`] construction and generation.
#[derive(Clone, Debug)]
#[non_exhaustive]
Expand Down
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
//!
//! ## Installation
//! ### Requirements
//! - Rust 1.56+
//! - Rust 1.60+
//!
//! ### Importing
//! This crate is available on [crates.io](https://crates.io/crates/bson). To use it in your application,
Expand Down Expand Up @@ -267,7 +267,7 @@
//!
//! ## Minimum supported Rust version (MSRV)
//!
//! The MSRV for this crate is currently 1.56.0. This will be rarely be increased, and if it ever is,
//! The MSRV for this crate is currently 1.60.0. This will be rarely be increased, and if it ever is,
//! it will only happen in a minor or major version release.

#![allow(clippy::cognitive_complexity, clippy::derive_partial_eq_without_eq)]
Expand Down
36 changes: 30 additions & 6 deletions src/uuid/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,21 +74,21 @@
//! For backwards compatibility, a `uuid-0_8` feature flag can be enabled, which provides the same
//! API for interoperation with version 0.8 of the `uuid` crate.
//!
//! ## The `serde_with` feature flag
//! ## The `serde_with-3` feature flag
//!
//! The `serde_with` feature can be enabled to support more ergonomic serde attributes for
//! The `serde_with-3` feature can be enabled to support more ergonomic serde attributes for
//! (de)serializing [`uuid::Uuid`] from/to BSON via the [`serde_with`](https://docs.rs/serde_with/1.11.0/serde_with/)
//! crate. The main benefit of this compared to the regular `serde_helpers` is that `serde_with` can
//! handle nested [`uuid::Uuid`] values (e.g. in [`Option`]), whereas the former only works on
//! crate. The main benefit of this compared to the regular `serde_helpers` is that `serde_with-3`
//! can handle nested [`uuid::Uuid`] values (e.g. in [`Option`]), whereas the former only works on
//! fields that are exactly [`uuid::Uuid`].
//! ```
//! # #[cfg(all(feature = "uuid-1", feature = "serde_with"))]
//! # #[cfg(all(feature = "uuid-1", feature = "serde_with-3"))]
//! # {
//! # use uuid as uuid;
//! use serde::{Deserialize, Serialize};
//! use bson::doc;
//!
//! #[serde_with::serde_as]
//! #[serde_with_3::serde_as]
//! #[derive(Deserialize, Serialize, PartialEq, Debug)]
//! struct Foo {
//! /// Serializes as a BSON binary rather than using [`uuid::Uuid`]'s serialization
Expand Down Expand Up @@ -509,6 +509,30 @@ macro_rules! trait_impls {
uuid.serialize(serializer)
}
}

#[cfg(all($feat, feature = "serde_with-3"))]
#[cfg_attr(docsrs, doc(cfg(all($feat, feature = "serde_with-3"))))]
impl<'de> serde_with_3::DeserializeAs<'de, $u> for crate::Uuid {
fn deserialize_as<D>(deserializer: D) -> std::result::Result<$u, D::Error>
where
D: serde::Deserializer<'de>,
{
let uuid = Uuid::deserialize(deserializer)?;
Ok(uuid.into())
}
}

#[cfg(all($feat, feature = "serde_with_3"))]
#[cfg_attr(docsrs, doc(cfg(all($feat, feature = "serde_with_3"))))]
impl serde_with_3::SerializeAs<$u> for crate::Uuid {
fn serialize_as<S>(source: &$u, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let uuid = Uuid::from(*source);
uuid.serialize(serializer)
}
}
};
}
trait_impls!(feature = "uuid-0_8", uuid_0_8::Uuid);
Expand Down