bson/
bson.rs

1//! A [BSON](https://bsonspec.org/spec.html) encoder
2//!
3//! This example demonstrates how to implement [`Encodable`] with custom
4//! errors, and how combinators can be used to simplify the implementation of
5//! encoders.
6//!
7//! Run the example with:
8//!
9//! ```sh
10//! cargo run --example bson
11//! ```
12
13use core::convert::Infallible;
14use core::num::TryFromIntError;
15
16use encode::combinators::{FromError, Iter, LengthPrefix, LE};
17use encode::encoders::InsufficientSpace;
18use encode::{Encodable, EncodableSize};
19
20/// A BSON encoding error.
21///
22/// This error type is used when encoding BSON fails. Because BSON has a maximum
23/// size of 2^31 - 1 bytes, we need to handle the case where the encoded BSON
24/// document is too large to fit in a 32-bit signed integer. Additionally, we
25/// need to handle the case where there is not enough space in the buffer to
26/// encode the BSON document, as well as any other errors that may occur during
27/// encoding.
28#[derive(Debug, Clone, PartialEq)]
29pub enum BsonError {
30    TooLarge(TryFromIntError),
31    InsufficientSpace(InsufficientSpace),
32}
33
34impl core::error::Error for BsonError {}
35impl core::fmt::Display for BsonError {
36    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
37        match self {
38            BsonError::TooLarge(_) => write!(f, "failed to encode BSON because it is too large"),
39            BsonError::InsufficientSpace(err) => core::fmt::Display::fmt(err, f),
40        }
41    }
42}
43impl From<TryFromIntError> for BsonError {
44    fn from(err: TryFromIntError) -> Self {
45        BsonError::TooLarge(err)
46    }
47}
48impl From<InsufficientSpace> for BsonError {
49    fn from(err: InsufficientSpace) -> Self {
50        BsonError::InsufficientSpace(err)
51    }
52}
53impl From<Infallible> for BsonError {
54    fn from(_: Infallible) -> Self {
55        unreachable!("infallible cannot be constructed")
56    }
57}
58
59/// A BSON document.
60#[derive(Debug, Clone, PartialEq)]
61pub struct BsonDocument {
62    /// The elements of the BSON document.
63    pub e_list: Vec<BsonElement>,
64}
65
66impl<Encoder> Encodable<Encoder> for BsonDocument
67where
68    Encoder: encode::ByteEncoder,
69    BsonError: From<Encoder::Error>,
70{
71    type Error = BsonError;
72
73    fn encode(&self, encoder: &mut Encoder) -> Result<(), Self::Error> {
74        let document = (
75            Iter::new(&self.e_list),
76            // u8 uses `BaseEncoder::Error`, so we need to convert it to our BsonError
77            // for our combinators to work.
78            FromError::<_, Self::Error>::new(0u8),
79        );
80        // We cannot use LengthPrefix here because we need to encode the size of
81        // the document including the size field itself.
82        let size = document.encoded_size()? + (i32::BITS / 8) as usize;
83        LE::<i32>::try_from(size)?.encode(encoder)?;
84        document.encode(encoder)?;
85        Ok(())
86    }
87}
88
89#[derive(Debug, Clone, PartialEq)]
90pub struct BsonElement {
91    /// The name of the BSON element.
92    pub e_value: String,
93    /// The variant of the BSON element.
94    pub variant: BsonElementVariant,
95}
96
97#[derive(Debug, Clone, PartialEq)]
98pub enum BsonElementVariant {
99    Double(f64),
100    String(String),
101    Document(BsonDocument),
102    Array(BsonDocument),
103    Binary { subtype: u8, data: Vec<u8> },
104    Undefined,
105    ObjectId([u8; 12]),
106    Boolean(bool),
107    DateTime(i64),
108    Null,
109    Regex(String, String),
110    DBPointer(String, [u8; 12]),
111    JavaScriptCode(String),
112    Symbol(String),
113    JavaScriptCodeWithScope(String, BsonDocument),
114    Int32(i32),
115    Int64(i64),
116    Timestamp(u64),
117    Decimal128([u8; 16]), // We don't have `f128` yet, so we use a byte array.
118    MinKey,
119    MaxKey,
120}
121
122impl<Encoder> Encodable<Encoder> for BsonElement
123where
124    Encoder: encode::ByteEncoder,
125    BsonError: From<Encoder::Error>,
126{
127    type Error = BsonError;
128
129    fn encode(&self, encoder: &mut Encoder) -> Result<(), Self::Error> {
130        let e_name = (&self.e_value, 0u8);
131
132        match &self.variant {
133            // When all elements have the same error type, we can use a tuple
134            // to encode them all at once.
135            BsonElementVariant::Double(x) => (1i8, e_name, LE::new(*x)).encode(encoder)?,
136            // However, when the error types are different, we need to either
137            // transform the error type using `FromError` or encode them separately.
138            BsonElementVariant::String(x) => {
139                2i8.encode(encoder)?;
140                e_name.encode(encoder)?;
141                BsonString(x).encode(encoder)?;
142            }
143            BsonElementVariant::Document(x) => {
144                3i8.encode(encoder)?;
145                e_name.encode(encoder)?;
146                x.encode(encoder)?;
147            }
148            BsonElementVariant::Array(x) => {
149                4i8.encode(encoder)?;
150                e_name.encode(encoder)?;
151                x.encode(encoder)?;
152            }
153            BsonElementVariant::Binary { subtype, data } => {
154                (5i8, e_name, LE::<i32>::try_from(data.len())?, subtype, data).encode(encoder)?
155            }
156            BsonElementVariant::Undefined => (6i8, e_name).encode(encoder)?,
157            BsonElementVariant::ObjectId(x) => (7i8, e_name, x).encode(encoder)?,
158            BsonElementVariant::Boolean(x) => (8i8, e_name, x).encode(encoder)?,
159            BsonElementVariant::DateTime(x) => (9i8, e_name, LE::new(*x)).encode(encoder)?,
160            BsonElementVariant::Null => (10i8, e_name).encode(encoder)?,
161            BsonElementVariant::Regex(x, y) => (11i8, e_name, x, 0u8, y, 0u8).encode(encoder)?,
162            BsonElementVariant::DBPointer(x, y) => {
163                (12i8, e_name).encode(encoder)?;
164                BsonString(x).encode(encoder)?;
165                y.encode(encoder)?;
166            }
167            BsonElementVariant::JavaScriptCode(x) => (13i8, e_name, x).encode(encoder)?,
168            BsonElementVariant::Symbol(x) => {
169                (14i8, e_name).encode(encoder)?;
170                BsonString(x).encode(encoder)?;
171            }
172            BsonElementVariant::JavaScriptCodeWithScope(x, y) => {
173                (15i8, e_name).encode(encoder)?;
174                let content = (BsonString(x), y);
175                // We cannot use LengthPrefix here because we need to encode the size of
176                // the content including the size field itself.
177                let len = content.encoded_size()? + (i32::BITS / 8) as usize;
178                LE::<i32>::try_from(len)?.encode(encoder)?;
179                content.encode(encoder)?;
180            }
181            BsonElementVariant::Int32(x) => (16i8, e_name, LE::new(*x)).encode(encoder)?,
182            BsonElementVariant::Timestamp(x) => (17i8, e_name, LE::new(*x)).encode(encoder)?,
183            BsonElementVariant::Int64(x) => (18i8, e_name, LE::new(*x)).encode(encoder)?,
184            BsonElementVariant::Decimal128(x) => (19i8, e_name, x).encode(encoder)?,
185            BsonElementVariant::MinKey => (-1i8, e_name).encode(encoder)?,
186            BsonElementVariant::MaxKey => (127i8, e_name).encode(encoder)?,
187        };
188        Ok(())
189    }
190}
191
192struct BsonString<S>(S);
193
194impl<S, Encoder> Encodable<Encoder> for BsonString<S>
195where
196    S: AsRef<str>,
197    Encoder: encode::ByteEncoder,
198    BsonError: From<Encoder::Error>,
199{
200    type Error = BsonError;
201
202    fn encode(&self, encoder: &mut Encoder) -> Result<(), Self::Error> {
203        LengthPrefix::<_, LE<i32>, BsonError>::new((self.0.as_ref(), 0u8)).encode(encoder)?;
204        Ok(())
205    }
206}
207
208fn main() -> Result<(), BsonError> {
209    let document = BsonDocument {
210        e_list: vec![
211            BsonElement {
212                e_value: "hello".into(),
213                variant: BsonElementVariant::Double(1.0),
214            },
215            BsonElement {
216                e_value: "world".into(),
217                variant: BsonElementVariant::String("hello".into()),
218            },
219            BsonElement {
220                e_value: "sub document".into(),
221                variant: BsonElementVariant::Document(BsonDocument {
222                    e_list: vec![BsonElement {
223                        e_value: "hello".into(),
224                        variant: BsonElementVariant::Double(1.0),
225                    }],
226                }),
227            },
228            BsonElement {
229                e_value: "array".into(),
230                variant: BsonElementVariant::Array(BsonDocument {
231                    e_list: vec![BsonElement {
232                        e_value: "0".into(), // BSON...
233                        variant: BsonElementVariant::Double(1.0),
234                    }],
235                }),
236            },
237        ],
238    };
239    let size = document.encoded_size()?;
240    println!("Expected BSON size: {}", size);
241
242    // We can also encode the JSON string into a buffer, like a Vec<u8> or &mut
243    // [u8].
244    let mut buf = Vec::with_capacity(size);
245    document.encode(&mut buf)?;
246
247    println!("{:?}", document);
248    println!("{:?}", buf.as_slice());
249    Ok(())
250}
251
252#[cfg(test)]
253mod official_examples {
254    //! Tests using official examples from BSON specification https://bsonspec.org/faq.html
255    use super::*;
256
257    #[test]
258    fn assert_that_hello_world_example_is_encoded_right() {
259        //! {"hello": "world"}
260        let expected = b"\x16\x00\x00\x00\x02hello\x00\x06\x00\x00\x00world\x00\x00";
261        let document = BsonDocument {
262            e_list: vec![BsonElement {
263                e_value: "hello".into(),
264                variant: BsonElementVariant::String("world".into()),
265            }],
266        };
267
268        let mut buf = Vec::new();
269        document.encode(&mut buf).unwrap();
270
271        assert_eq!(buf.as_slice(), expected);
272    }
273    #[test]
274    fn assert_that_awesome_example_is_encoded_right() {
275        //! {"BSON": ["awesome", 5.05, 1986]}
276        let expected = b"\x31\x00\x00\x00\x04BSON\x00\x26\x00\x00\x00\x02\x30\x00\x08\x00\x00\x00awesome\x00\x01\x31\x00\x33\x33\x33\x33\x33\x33\x14\x40\x10\x32\x00\xc2\x07\x00\x00\x00\x00";
277        let document = BsonDocument {
278            e_list: vec![BsonElement {
279                e_value: "BSON".into(),
280                variant: BsonElementVariant::Array(BsonDocument {
281                    e_list: vec![
282                        BsonElement {
283                            e_value: "0".into(),
284                            variant: BsonElementVariant::String("awesome".into()),
285                        },
286                        BsonElement {
287                            e_value: "1".into(),
288                            variant: BsonElementVariant::Double(5.05),
289                        },
290                        BsonElement {
291                            e_value: "2".into(),
292                            variant: BsonElementVariant::Int32(1986),
293                        },
294                    ],
295                }),
296            }],
297        };
298
299        let mut buf = Vec::new();
300        document.encode(&mut buf).unwrap();
301
302        assert_eq!(buf.as_slice(), expected);
303    }
304}