Skip to content

Commit 982b15e

Browse files
authored
RUST-756 Replace compat::u2f with serde helpers (#254)
1 parent 0d31401 commit 982b15e

File tree

6 files changed

+128
-136
lines changed

6 files changed

+128
-136
lines changed

README.md

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ Encoding and decoding support for BSON in Rust
1111
- [BSON Values](#bson-values)
1212
- [BSON Documents](#bson-documents)
1313
- [Modeling BSON with strongly typed data structures](#modeling-bson-with-strongly-typed-data-structures)
14-
- [Breaking Changes](#breaking-changes)
1514
- [Contributing](#contributing)
1615
- [Running the Tests](#running-the-tests)
1716
- [Continuous Integration](#continuous-integration)
@@ -185,32 +184,6 @@ separate the "business logic" that operates over the data from the (de)serializa
185184
translates the data to/from its serialized form. This can lead to more clear and concise code
186185
that is also less error prone.
187186

188-
## Breaking Changes
189-
190-
In the BSON specification, _unsigned integer types_ are unsupported; for example, `u32`. In the older version of this crate (< `v0.8.0`), if you uses `serde` to serialize _unsigned integer types_ into BSON, it will store them with `Bson::Double` type. From `v0.8.0`, we removed this behavior and simply returned an error when you want to serialize _unsigned integer types_ to BSON. [#72](https://github.com/zonyitoo/bson-rs/pull/72)
191-
192-
For backward compatibility, we've provided a mod `bson::compat::u2f` to explicitly serialize _unsigned integer types_ into BSON's floating point value as follows:
193-
194-
```rust
195-
#[test]
196-
fn test_compat_u2f() {
197-
#[derive(Serialize, Deserialize, Eq, PartialEq, Debug)]
198-
struct Foo {
199-
#[serde(with = "bson::compat::u2f")]
200-
x: u32
201-
}
202-
203-
let foo = Foo { x: 20 };
204-
let b = bson::to_bson(&foo).unwrap();
205-
assert_eq!(b, Bson::Document(doc! { "x": Bson::Double(20.0) }));
206-
207-
let de_foo = bson::from_bson::<Foo>(b).unwrap();
208-
assert_eq!(de_foo, foo);
209-
}
210-
```
211-
212-
In this example, we added an attribute `#[serde(with = "bson::compat::u2f")]` on field `x`, which will tell `serde` to use the `bson::compat::u2f::serialize` and `bson::compat::u2f::deserialize` methods to process this field.
213-
214187
## Contributing
215188

216189
We encourage and would happily accept contributions in the form of GitHub pull requests. Before opening one, be sure to run the tests locally; check out the [testing section](#running-the-tests) for information on how to do that. Once you open a pull request, your branch will be run against the same testing matrix that we use for our [continuous integration](#continuous-integration) system, so it is usually sufficient to only run the integration tests locally against a standalone. Remember to always run the linter tests before opening a pull request.

src/compat/mod.rs

Lines changed: 0 additions & 3 deletions
This file was deleted.

src/compat/u2f.rs

Lines changed: 0 additions & 81 deletions
This file was deleted.

src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,6 @@ pub use self::{
204204
#[macro_use]
205205
mod macros;
206206
mod bson;
207-
pub mod compat;
208207
pub mod de;
209208
pub mod decimal128;
210209
pub mod document;

src/serde_helpers.rs

Lines changed: 89 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,18 @@ pub use iso_string_as_bson_datetime::{
2121
serialize as serialize_iso_string_as_bson_datetime,
2222
};
2323
pub use timestamp_as_u32::{
24-
deserialize as deserialize_timestamp_from_u32, serialize as serialize_timestamp_as_u32,
24+
deserialize as deserialize_timestamp_from_u32,
25+
serialize as serialize_timestamp_as_u32,
2526
};
27+
pub use u32_as_f64::{deserialize as deserialize_u32_from_f64, serialize as serialize_u32_as_f64};
2628
pub use u32_as_timestamp::{
27-
deserialize as deserialize_u32_from_timestamp, serialize as serialize_u32_as_timestamp,
29+
deserialize as deserialize_u32_from_timestamp,
30+
serialize as serialize_u32_as_timestamp,
2831
};
32+
pub use u64_as_f64::{deserialize as deserialize_u64_from_f64, serialize as serialize_u64_as_f64};
2933
pub use uuid_as_binary::{
30-
deserialize as deserialize_uuid_from_binary, serialize as serialize_uuid_as_binary,
34+
deserialize as deserialize_uuid_from_binary,
35+
serialize as serialize_uuid_as_binary,
3136
};
3237

3338
/// Attempts to serialize a u32 as an i32. Errors if an exact conversion is not possible.
@@ -59,6 +64,87 @@ pub fn serialize_u64_as_i64<S: Serializer>(val: &u64, serializer: S) -> Result<S
5964
}
6065
}
6166

67+
/// Contains functions to serialize a u32 as an f64 (BSON double) and deserialize a
68+
/// u32 from an f64 (BSON double).
69+
///
70+
/// ```rust
71+
/// # use serde::{Serialize, Deserialize};
72+
/// # use bson::serde_helpers::u32_as_f64;
73+
/// #[derive(Serialize, Deserialize)]
74+
/// struct FileInfo {
75+
/// #[serde(with = "u32_as_f64")]
76+
/// pub size_bytes: u32,
77+
/// }
78+
/// ```
79+
pub mod u32_as_f64 {
80+
use serde::{de, Deserialize, Deserializer, Serializer};
81+
82+
/// Deserializes a u32 from an f64 (BSON double). Errors if an exact conversion is not possible.
83+
pub fn deserialize<'de, D>(deserializer: D) -> Result<u32, D::Error>
84+
where
85+
D: Deserializer<'de>,
86+
{
87+
let f = f64::deserialize(deserializer)?;
88+
if (f - f as u32 as f64).abs() <= f64::EPSILON {
89+
Ok(f as u32)
90+
} else {
91+
Err(de::Error::custom(format!(
92+
"cannot convert f64 (BSON double) {} to u32",
93+
f
94+
)))
95+
}
96+
}
97+
98+
/// Serializes a u32 as an f64 (BSON double).
99+
pub fn serialize<S: Serializer>(val: &u32, serializer: S) -> Result<S::Ok, S::Error> {
100+
serializer.serialize_f64(*val as f64)
101+
}
102+
}
103+
104+
/// Contains functions to serialize a u64 as an f64 (BSON double) and deserialize a
105+
/// u64 from an f64 (BSON double).
106+
///
107+
/// ```rust
108+
/// # use serde::{Serialize, Deserialize};
109+
/// # use bson::serde_helpers::u64_as_f64;
110+
/// #[derive(Serialize, Deserialize)]
111+
/// struct FileInfo {
112+
/// #[serde(with = "u64_as_f64")]
113+
/// pub size_bytes: u64,
114+
/// }
115+
/// ```
116+
pub mod u64_as_f64 {
117+
use serde::{de, ser, Deserialize, Deserializer, Serializer};
118+
119+
/// Deserializes a u64 from an f64 (BSON double). Errors if an exact conversion is not possible.
120+
pub fn deserialize<'de, D>(deserializer: D) -> Result<u64, D::Error>
121+
where
122+
D: Deserializer<'de>,
123+
{
124+
let f = f64::deserialize(deserializer)?;
125+
if (f - f as u64 as f64).abs() <= f64::EPSILON {
126+
Ok(f as u64)
127+
} else {
128+
Err(de::Error::custom(format!(
129+
"cannot convert f64 (BSON double) {} to u64",
130+
f
131+
)))
132+
}
133+
}
134+
135+
/// Serializes a u64 as an f64 (BSON double). Errors if an exact conversion is not possible.
136+
pub fn serialize<S: Serializer>(val: &u64, serializer: S) -> Result<S::Ok, S::Error> {
137+
if val < &u64::MAX && *val == *val as f64 as u64 {
138+
serializer.serialize_f64(*val as f64)
139+
} else {
140+
Err(ser::Error::custom(format!(
141+
"cannot convert u64 {} to f64 (BSON double)",
142+
val
143+
)))
144+
}
145+
}
146+
}
147+
62148
/// Contains functions to serialize a chrono::DateTime as a bson::DateTime and deserialize a
63149
/// chrono::DateTime from a bson::DateTime.
64150
///

src/tests/serde.rs

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,15 @@
22

33
use crate::{
44
bson,
5-
compat::u2f,
65
doc,
76
from_bson,
87
from_document,
98
oid::ObjectId,
109
serde_helpers,
1110
serde_helpers::{
12-
hex_string_as_object_id,
1311
bson_datetime_as_iso_string,
1412
chrono_datetime_as_bson_datetime,
13+
hex_string_as_object_id,
1514
iso_string_as_bson_datetime,
1615
timestamp_as_u32,
1716
u32_as_timestamp,
@@ -274,23 +273,6 @@ fn test_ser_datetime() {
274273
assert_eq!(xfoo, foo);
275274
}
276275

277-
#[test]
278-
fn test_compat_u2f() {
279-
let _guard = LOCK.run_concurrently();
280-
#[derive(Serialize, Deserialize, Eq, PartialEq, Debug)]
281-
struct Foo {
282-
#[serde(with = "u2f")]
283-
x: u32,
284-
}
285-
286-
let foo = Foo { x: 20 };
287-
let b = to_bson(&foo).unwrap();
288-
assert_eq!(b, Bson::Document(doc! { "x": (Bson::Double(20.0)) }));
289-
290-
let de_foo = from_bson::<Foo>(b).unwrap();
291-
assert_eq!(de_foo, foo);
292-
}
293-
294276
#[test]
295277
fn test_binary_generic_roundtrip() {
296278
let _guard = LOCK.run_concurrently();
@@ -664,6 +646,40 @@ fn test_unsigned_helpers() {
664646
};
665647
let doc_result = to_document(&b);
666648
assert!(doc_result.is_err());
649+
650+
#[derive(Deserialize, Serialize, Debug, PartialEq)]
651+
struct F {
652+
#[serde(with = "serde_helpers::u32_as_f64")]
653+
num_1: u32,
654+
#[serde(with = "serde_helpers::u64_as_f64")]
655+
num_2: u64,
656+
}
657+
658+
let f = F {
659+
num_1: 101,
660+
num_2: 12345,
661+
};
662+
let doc = to_document(&f).unwrap();
663+
assert!((doc.get_f64("num_1").unwrap() - 101.0).abs() < f64::EPSILON);
664+
assert!((doc.get_f64("num_2").unwrap() - 12345.0).abs() < f64::EPSILON);
665+
666+
let back: F = from_document(doc).unwrap();
667+
assert_eq!(back, f);
668+
669+
let f = F {
670+
num_1: 1,
671+
// f64 cannot represent many large integers exactly, u64::MAX included
672+
num_2: u64::MAX,
673+
};
674+
let doc_result = to_document(&f);
675+
assert!(doc_result.is_err());
676+
677+
let f = F {
678+
num_1: 1,
679+
num_2: u64::MAX - 255,
680+
};
681+
let doc_result = to_document(&f);
682+
assert!(doc_result.is_err());
667683
}
668684

669685
#[test]
@@ -735,9 +751,11 @@ fn test_oid_helpers() {
735751
}
736752

737753
let oid = ObjectId::new();
738-
let a = A { oid: oid.to_string() };
754+
let a = A {
755+
oid: oid.to_string(),
756+
};
739757
let doc = to_document(&a).unwrap();
740-
assert_eq!(doc.get_object_id("oid").unwrap(), oid);
758+
assert_eq!(doc.get_object_id("oid").unwrap(), oid);
741759
let a: A = from_document(doc).unwrap();
742760
assert_eq!(a.oid, oid.to_string());
743761
}

0 commit comments

Comments
 (0)