diff --git a/src/de/mod.rs b/src/de/mod.rs index 18e8232d..9383e2b3 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -355,3 +355,11 @@ where let de = Deserializer::new(bson); Deserialize::deserialize(de) } + +/// Decode a BSON `Document` into a `T` Deserializable. +pub fn from_document(doc: Document) -> Result +where + T: DeserializeOwned, +{ + from_bson(Bson::Document(doc)) +} diff --git a/src/lib.rs b/src/lib.rs index 2865673d..adf55f4a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -196,9 +196,9 @@ pub use self::{ Regex, Timestamp, }, - de::{from_bson, Deserializer}, + de::{from_bson, from_document, Deserializer}, decimal128::Decimal128, - ser::{to_bson, Serializer}, + ser::{to_bson, to_document, Serializer}, }; #[macro_use] diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 04b4f6b1..3fb14ad7 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -36,7 +36,7 @@ use chrono::Timelike; #[cfg(feature = "decimal128")] use crate::decimal128::Decimal128; use crate::{ - bson::{Binary, Bson, DbPointer, JavaScriptCodeWithScope, Regex}, + bson::{Binary, Bson, DbPointer, Document, JavaScriptCodeWithScope, Regex}, spec::BinarySubtype, }; use ::serde::Serialize; @@ -189,3 +189,19 @@ where let ser = Serializer::new(); value.serialize(ser) } + +/// Encode a `T` Serializable into a BSON `Document`. +pub fn to_document(value: &T) -> Result +where + T: Serialize, +{ + match to_bson(value)? { + Bson::Document(doc) => Ok(doc), + bson => Err(Error::SerializationError { + message: format!( + "Could not be serialized to Document, got {:?} instead", + bson.element_type() + ), + }), + } +} diff --git a/src/tests/modules/ser.rs b/src/tests/modules/ser.rs index 46504dc8..63983b6e 100644 --- a/src/tests/modules/ser.rs +++ b/src/tests/modules/ser.rs @@ -1,8 +1,10 @@ +use std::{collections::BTreeMap, u16, u32, u64, u8}; + +use assert_matches::assert_matches; + #[cfg(feature = "decimal128")] use crate::decimal128::Decimal128; use crate::{from_bson, oid::ObjectId, ser, tests::LOCK, to_bson, Bson}; -use assert_matches::assert_matches; -use std::{collections::BTreeMap, u16, u32, u64, u8}; #[test] #[allow(clippy::float_cmp)] diff --git a/src/tests/modules/serializer_deserializer.rs b/src/tests/modules/serializer_deserializer.rs index 430f1a6f..d42ac8ff 100644 --- a/src/tests/modules/serializer_deserializer.rs +++ b/src/tests/modules/serializer_deserializer.rs @@ -3,13 +3,18 @@ use std::{ io::{Cursor, Write}, }; +use serde::{Deserialize, Serialize}; + #[cfg(feature = "decimal128")] use crate::decimal128::Decimal128; use crate::{ + de::from_document, doc, oid::ObjectId, + ser::Error, spec::BinarySubtype, tests::LOCK, + to_document, Binary, Bson, Document, @@ -470,3 +475,53 @@ fn test_serialize_deserialize_db_pointer() { let deserialized = Document::from_reader(&mut Cursor::new(buf)).unwrap(); assert_eq!(deserialized, doc); } + +#[test] +fn test_serialize_deserialize_document() { + let _guard = LOCK.run_concurrently(); + + #[derive(Debug, Deserialize, Serialize, PartialEq)] + struct Point { + x: i32, + y: i32, + } + let src = Point { x: 1, y: 2 }; + + let doc = to_document(&src).unwrap(); + assert_eq!(doc, doc! { "x": 1, "y": 2 }); + + let point: Point = from_document(doc).unwrap(); + assert_eq!(src, point); + + #[derive(Debug, Deserialize, Serialize, PartialEq)] + struct Line { + p1: Point, + p2: Point, + } + let src = Line { + p1: Point { x: 0, y: 0 }, + p2: Point { x: 1, y: 1 }, + }; + + let doc = to_document(&src).unwrap(); + assert_eq!( + doc, + doc! { "p1": { "x": 0, "y": 0 }, "p2": { "x": 1, "y": 1 } } + ); + + let line: Line = from_document(doc).unwrap(); + assert_eq!(src, line); + + let x = 1; + let err = to_document(&x).unwrap_err(); + match err { + Error::SerializationError { message } => { + assert!(message.contains("Could not be serialized to Document")); + } + e => panic!("expected SerializationError, got {}", e), + } + + let bad_point = doc! { "x": "one", "y": "two" }; + let bad_point: Result = from_document(bad_point); + assert!(bad_point.is_err()); +}