Skip to content

Commit e03e95e

Browse files
RUST-506 Add serde helper functions (#222)
1 parent 55abdff commit e03e95e

File tree

4 files changed

+524
-2
lines changed

4 files changed

+524
-2
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ hex = "0.4.2"
4848
decimal = { version = "2.0.4", default_features = false, optional = true }
4949
base64 = "0.12.1"
5050
lazy_static = "1.4.0"
51+
uuid = "0.8.1"
5152

5253
[dev-dependencies]
5354
assert_matches = "1.2"

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ pub mod document;
211211
pub mod extjson;
212212
pub mod oid;
213213
pub mod ser;
214+
pub mod serde_helpers;
214215
pub mod spec;
215216

216217
#[cfg(test)]

src/serde_helpers.rs

Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
//! Collection of helper functions for serializing to and deserializing from BSON using Serde
2+
3+
use std::{convert::TryFrom, result::Result};
4+
5+
use serde::{ser, Serialize, Serializer};
6+
7+
use crate::oid::ObjectId;
8+
9+
pub use bson_datetime_as_iso_string::{
10+
deserialize as deserialize_bson_datetime_from_iso_string,
11+
serialize as serialize_bson_datetime_as_iso_string,
12+
};
13+
pub use chrono_datetime_as_bson_datetime::{
14+
deserialize as deserialize_chrono_datetime_from_bson_datetime,
15+
serialize as serialize_chrono_datetime_as_bson_datetime,
16+
};
17+
pub use iso_string_as_bson_datetime::{
18+
deserialize as deserialize_iso_string_from_bson_datetime,
19+
serialize as serialize_iso_string_as_bson_datetime,
20+
};
21+
pub use timestamp_as_u32::{
22+
deserialize as deserialize_timestamp_from_u32,
23+
serialize as serialize_timestamp_as_u32,
24+
};
25+
pub use u32_as_timestamp::{
26+
deserialize as deserialize_u32_from_timestamp,
27+
serialize as serialize_u32_as_timestamp,
28+
};
29+
pub use uuid_as_binary::{
30+
deserialize as deserialize_uuid_from_binary,
31+
serialize as serialize_uuid_as_binary,
32+
};
33+
34+
/// Attempts to serialize a u32 as an i32. Errors if an exact conversion is not possible.
35+
pub fn serialize_u32_as_i32<S: Serializer>(val: &u32, serializer: S) -> Result<S::Ok, S::Error> {
36+
match i32::try_from(*val) {
37+
Ok(val) => serializer.serialize_i32(val),
38+
Err(_) => Err(ser::Error::custom(format!("cannot convert {} to i32", val))),
39+
}
40+
}
41+
42+
/// Serializes a u32 as an i64.
43+
pub fn serialize_u32_as_i64<S: Serializer>(val: &u32, serializer: S) -> Result<S::Ok, S::Error> {
44+
serializer.serialize_i64(*val as i64)
45+
}
46+
47+
/// Attempts to serialize a u64 as an i32. Errors if an exact conversion is not possible.
48+
pub fn serialize_u64_as_i32<S: Serializer>(val: &u64, serializer: S) -> Result<S::Ok, S::Error> {
49+
match i32::try_from(*val) {
50+
Ok(val) => serializer.serialize_i32(val),
51+
Err(_) => Err(ser::Error::custom(format!("cannot convert {} to i32", val))),
52+
}
53+
}
54+
55+
/// Attempts to serialize a u64 as an i64. Errors if an exact conversion is not possible.
56+
pub fn serialize_u64_as_i64<S: Serializer>(val: &u64, serializer: S) -> Result<S::Ok, S::Error> {
57+
match i64::try_from(*val) {
58+
Ok(val) => serializer.serialize_i64(val),
59+
Err(_) => Err(ser::Error::custom(format!("cannot convert {} to i64", val))),
60+
}
61+
}
62+
63+
/// Contains functions to serialize a chrono::DateTime as a bson::DateTime and deserialize a
64+
/// chrono::DateTime from a bson::DateTime.
65+
///
66+
/// ```rust
67+
/// # use serde::{Serialize, Deserialize};
68+
/// # use bson::serde_helpers::chrono_datetime_as_bson_datetime;
69+
/// #[derive(Serialize, Deserialize)]
70+
/// struct Event {
71+
/// #[serde(with = "chrono_datetime_as_bson_datetime")]
72+
/// pub date: chrono::DateTime<chrono::Utc>,
73+
/// }
74+
/// ```
75+
pub mod chrono_datetime_as_bson_datetime {
76+
use crate::DateTime;
77+
use chrono::Utc;
78+
use serde::{Deserialize, Deserializer, Serialize, Serializer};
79+
use std::result::Result;
80+
81+
/// Deserializes a chrono::DateTime from a bson::DateTime.
82+
pub fn deserialize<'de, D>(deserializer: D) -> Result<chrono::DateTime<Utc>, D::Error>
83+
where
84+
D: Deserializer<'de>,
85+
{
86+
let datetime = DateTime::deserialize(deserializer)?;
87+
Ok(datetime.into())
88+
}
89+
90+
/// Serializes a chrono::DateTime as a bson::DateTime.
91+
pub fn serialize<S: Serializer>(
92+
val: &chrono::DateTime<Utc>,
93+
serializer: S,
94+
) -> Result<S::Ok, S::Error> {
95+
let datetime = DateTime::from(val.to_owned());
96+
datetime.serialize(serializer)
97+
}
98+
}
99+
100+
/// Contains functions to serialize an ISO string as a bson::DateTime and deserialize an ISO string
101+
/// from a bson::DateTime.
102+
///
103+
/// ```rust
104+
/// # use serde::{Serialize, Deserialize};
105+
/// # use bson::serde_helpers::iso_string_as_bson_datetime;
106+
/// #[derive(Serialize, Deserialize)]
107+
/// struct Event {
108+
/// #[serde(with = "iso_string_as_bson_datetime")]
109+
/// pub date: String,
110+
/// }
111+
/// ```
112+
pub mod iso_string_as_bson_datetime {
113+
use crate::{Bson, DateTime};
114+
use serde::{ser, Deserialize, Deserializer, Serialize, Serializer};
115+
use std::{result::Result, str::FromStr};
116+
117+
/// Deserializes an ISO string from a DateTime.
118+
pub fn deserialize<'de, D>(deserializer: D) -> Result<String, D::Error>
119+
where
120+
D: Deserializer<'de>,
121+
{
122+
let date = DateTime::deserialize(deserializer)?;
123+
Ok(date.to_string())
124+
}
125+
126+
/// Serializes an ISO string as a DateTime.
127+
pub fn serialize<S: Serializer>(val: &str, serializer: S) -> Result<S::Ok, S::Error> {
128+
let date = chrono::DateTime::from_str(val).map_err(|_| {
129+
ser::Error::custom(format!("cannot convert {} to chrono::DateTime", val))
130+
})?;
131+
Bson::DateTime(date).serialize(serializer)
132+
}
133+
}
134+
135+
/// Contains functions to serialize a bson::DateTime as an ISO string and deserialize a
136+
/// bson::DateTime from an ISO string.
137+
///
138+
/// ```rust
139+
/// # use serde::{Serialize, Deserialize};
140+
/// # use bson::serde_helpers::bson_datetime_as_iso_string;
141+
/// #[derive(Serialize, Deserialize)]
142+
/// struct Event {
143+
/// #[serde(with = "bson_datetime_as_iso_string")]
144+
/// pub date: bson::DateTime,
145+
/// }
146+
/// ```
147+
pub mod bson_datetime_as_iso_string {
148+
use crate::DateTime;
149+
use serde::{de, Deserialize, Deserializer, Serializer};
150+
use std::{result::Result, str::FromStr};
151+
152+
/// Deserializes a bson::DateTime from an ISO string.
153+
pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTime, D::Error>
154+
where
155+
D: Deserializer<'de>,
156+
{
157+
let iso = String::deserialize(deserializer)?;
158+
let date = chrono::DateTime::from_str(&iso).map_err(|_| {
159+
de::Error::custom(format!("cannot convert {} to chrono::DateTime", iso))
160+
})?;
161+
Ok(DateTime::from(date))
162+
}
163+
164+
/// Serializes a bson::DateTime as an ISO string.
165+
pub fn serialize<S: Serializer>(val: &DateTime, serializer: S) -> Result<S::Ok, S::Error> {
166+
serializer.serialize_str(&val.to_string())
167+
}
168+
}
169+
170+
/// Serializes a hex string as an ObjectId.
171+
pub fn serialize_hex_string_as_object_id<S: Serializer>(
172+
val: &str,
173+
serializer: S,
174+
) -> Result<S::Ok, S::Error> {
175+
match ObjectId::with_string(val) {
176+
Ok(oid) => oid.serialize(serializer),
177+
Err(_) => Err(ser::Error::custom(format!(
178+
"cannot convert {} to ObjectId",
179+
val
180+
))),
181+
}
182+
}
183+
184+
/// Contains functions to serialize a Uuid as a bson::Binary and deserialize a Uuid from a
185+
/// bson::Binary.
186+
///
187+
/// ```rust
188+
/// # use serde::{Serialize, Deserialize};
189+
/// # use uuid::Uuid;
190+
/// # use bson::serde_helpers::uuid_as_binary;
191+
/// #[derive(Serialize, Deserialize)]
192+
/// struct Item {
193+
/// #[serde(with = "uuid_as_binary")]
194+
/// pub id: Uuid,
195+
/// }
196+
/// ```
197+
pub mod uuid_as_binary {
198+
use crate::{spec::BinarySubtype, Binary};
199+
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
200+
use std::result::Result;
201+
use uuid::Uuid;
202+
203+
/// Serializes a Uuid as a Binary.
204+
pub fn serialize<S: Serializer>(val: &Uuid, serializer: S) -> Result<S::Ok, S::Error> {
205+
let binary = Binary {
206+
subtype: BinarySubtype::Uuid,
207+
bytes: val.as_bytes().to_vec(),
208+
};
209+
binary.serialize(serializer)
210+
}
211+
212+
/// Deserializes a Uuid from a Binary.
213+
pub fn deserialize<'de, D>(deserializer: D) -> Result<Uuid, D::Error>
214+
where
215+
D: Deserializer<'de>,
216+
{
217+
let binary = Binary::deserialize(deserializer)?;
218+
if binary.subtype == BinarySubtype::Uuid {
219+
if binary.bytes.len() == 16 {
220+
let mut bytes = [0u8; 16];
221+
bytes.copy_from_slice(&binary.bytes);
222+
Ok(Uuid::from_bytes(bytes))
223+
} else {
224+
Err(de::Error::custom(
225+
"cannot convert Binary to Uuid: incorrect bytes length",
226+
))
227+
}
228+
} else {
229+
Err(de::Error::custom(
230+
"cannot convert Binary to Uuid: incorrect binary subtype",
231+
))
232+
}
233+
}
234+
}
235+
236+
/// Contains functions to serialize a u32 as a bson::Timestamp and deserialize a u32 from a
237+
/// bson::Timestamp. The u32 should represent seconds since the Unix epoch.
238+
///
239+
/// ```rust
240+
/// # use serde::{Serialize, Deserialize};
241+
/// # use bson::serde_helpers::u32_as_timestamp;
242+
/// #[derive(Serialize, Deserialize)]
243+
/// struct Event {
244+
/// #[serde(with = "u32_as_timestamp")]
245+
/// pub time: u32,
246+
/// }
247+
/// ```
248+
pub mod u32_as_timestamp {
249+
use crate::{Bson, Timestamp};
250+
use serde::{Deserialize, Deserializer, Serialize, Serializer};
251+
use std::result::Result;
252+
253+
/// Serializes a u32 as a bson::Timestamp.
254+
pub fn serialize<S: Serializer>(val: &u32, serializer: S) -> Result<S::Ok, S::Error> {
255+
let timestamp = Bson::Timestamp(Timestamp {
256+
time: *val,
257+
increment: 0,
258+
});
259+
timestamp.serialize(serializer)
260+
}
261+
262+
/// Deserializes a u32 from a bson::Timestamp.
263+
pub fn deserialize<'de, D>(deserializer: D) -> Result<u32, D::Error>
264+
where
265+
D: Deserializer<'de>,
266+
{
267+
let timestamp = Timestamp::deserialize(deserializer)?;
268+
Ok(timestamp.time)
269+
}
270+
}
271+
272+
/// Contains functions to serialize a bson::Timestamp as a u32 and deserialize a bson::Timestamp
273+
/// from a u32. The u32 should represent seconds since the Unix epoch. Serialization will return an
274+
/// error if the Timestamp has a non-zero increment.
275+
///
276+
/// ```rust
277+
/// # use serde::{Serialize, Deserialize};
278+
/// # use bson::{serde_helpers::timestamp_as_u32, Timestamp};
279+
/// #[derive(Serialize, Deserialize)]
280+
/// struct Item {
281+
/// #[serde(with = "timestamp_as_u32")]
282+
/// pub timestamp: Timestamp,
283+
/// }
284+
/// ```
285+
pub mod timestamp_as_u32 {
286+
use crate::Timestamp;
287+
use serde::{ser, Deserialize, Deserializer, Serializer};
288+
use std::result::Result;
289+
290+
/// Serializes a bson::Timestamp as a u32. Returns an error if the conversion is lossy (i.e. the
291+
/// Timestamp has a non-zero increment).
292+
pub fn serialize<S: Serializer>(val: &Timestamp, serializer: S) -> Result<S::Ok, S::Error> {
293+
if val.increment != 0 {
294+
return Err(ser::Error::custom(
295+
"Cannot convert Timestamp with a non-zero increment to u32",
296+
));
297+
}
298+
serializer.serialize_u32(val.time)
299+
}
300+
301+
/// Deserializes a bson::Timestamp from a u32.
302+
pub fn deserialize<'de, D>(deserializer: D) -> Result<Timestamp, D::Error>
303+
where
304+
D: Deserializer<'de>,
305+
{
306+
let time = u32::deserialize(deserializer)?;
307+
Ok(Timestamp { time, increment: 0 })
308+
}
309+
}

0 commit comments

Comments
 (0)