rbson/
bson.rs

1// The MIT License (MIT)
2
3// Copyright (c) 2015 Y. T. Chung <zonyitoo@gmail.com>
4
5// Permission is hereby granted, free of charge, to any person obtaining a copy of
6// this software and associated documentation files (the "Software"), to deal in
7// the Software without restriction, including without limitation the rights to
8// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9// the Software, and to permit persons to whom the Software is furnished to do so,
10// subject to the following conditions:
11
12// The above copyright notice and this permission notice shall be included in all
13// copies or substantial portions of the Software.
14
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22//! BSON definition
23
24use std::{
25    convert::{TryFrom, TryInto},
26    fmt::{self, Debug, Display, Formatter},
27};
28
29use chrono::Datelike;
30use serde_json::{json, Value};
31
32pub use crate::document::Document;
33use crate::{
34    oid::{self, ObjectId},
35    spec::{BinarySubtype, ElementType},
36    Decimal128,
37};
38
39/// Possible BSON value types.
40#[derive(Clone, PartialEq)]
41pub enum Bson {
42    /// 64-bit binary floating point
43    Double(f64),
44    /// UTF-8 string
45    String(String),
46    /// Array
47    Array(Array),
48    /// Embedded document
49    Document(Document),
50    /// Boolean value
51    Boolean(bool),
52    /// Null value
53    Null,
54    /// 32-bit signed integer
55    Int32(i32),
56    /// 64-bit signed integer
57    Int64(i64),
58    /// 32-bit signed integer
59    UInt32(u32),
60    /// 64-bit signed integer
61    UInt64(u64),
62    /// Timestamp
63    Timestamp(Timestamp),
64    /// Binary data
65    Binary(Binary),
66    /// UTC datetime
67    DateTime(crate::DateTime),
68    /// [128-bit decimal floating point](https://github.com/mongodb/specifications/blob/master/source/bson-decimal128/decimal128.rst)
69    Decimal128(Decimal128),
70}
71
72/// Alias for `Vec<Bson>`.
73pub type Array = Vec<Bson>;
74
75impl Default for Bson {
76    fn default() -> Self {
77        Bson::Null
78    }
79}
80
81impl Display for Bson {
82    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
83        match *self {
84            Bson::Double(f) => write!(fmt, "{}", f),
85            Bson::String(ref s) => write!(fmt, "\"{}\"", s),
86            Bson::Array(ref vec) => {
87                fmt.write_str("[")?;
88
89                let mut first = true;
90                for bson in vec {
91                    if !first {
92                        fmt.write_str(", ")?;
93                    }
94
95                    write!(fmt, "{}", bson)?;
96                    first = false;
97                }
98
99                fmt.write_str("]")
100            }
101            Bson::Document(ref doc) => write!(fmt, "{}", doc),
102            Bson::Boolean(b) => write!(fmt, "{}", b),
103            Bson::Null => write!(fmt, "null"),
104            Bson::Int32(i) => write!(fmt, "{}", i),
105            Bson::Int64(i) => write!(fmt, "{}", i),
106            Bson::UInt32(i) => write!(fmt, "{}", i),
107            Bson::UInt64(i) => write!(fmt, "{}", i),
108            Bson::Timestamp(ref x) => write!(fmt, "{}", x),
109            Bson::Binary(ref x) => write!(fmt, "{}", x),
110            Bson::DateTime(date_time) => write!(fmt, "DateTime(\"{}\")", date_time),
111            Bson::Decimal128(ref d) => write!(fmt, "{}", d),
112        }
113    }
114}
115
116impl Debug for Bson {
117    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
118        match *self {
119            Bson::Double(f) => fmt.debug_tuple("Double").field(&f).finish(),
120            Bson::String(ref s) => fmt.debug_tuple("String").field(s).finish(),
121            Bson::Array(ref vec) => {
122                write!(fmt, "Array(")?;
123                Debug::fmt(vec, fmt)?;
124                write!(fmt, ")")
125            }
126            Bson::Document(ref doc) => Debug::fmt(doc, fmt),
127            Bson::Boolean(b) => fmt.debug_tuple("Boolean").field(&b).finish(),
128            Bson::Null => write!(fmt, "Null"),
129            Bson::Int32(i) => fmt.debug_tuple("Int32").field(&i).finish(),
130            Bson::Int64(i) => fmt.debug_tuple("Int64").field(&i).finish(),
131            Bson::UInt32(i) => fmt.debug_tuple("UInt32").field(&i).finish(),
132            Bson::UInt64(i) => fmt.debug_tuple("UInt64").field(&i).finish(),
133            Bson::Timestamp(ref t) => Debug::fmt(t, fmt),
134            Bson::Binary(ref b) => Debug::fmt(b, fmt),
135            Bson::DateTime(ref date_time) => Debug::fmt(date_time, fmt),
136            Bson::Decimal128(ref d) => Debug::fmt(d, fmt),
137        }
138    }
139}
140
141impl From<f32> for Bson {
142    fn from(a: f32) -> Bson {
143        Bson::Double(a.into())
144    }
145}
146
147impl From<f64> for Bson {
148    fn from(a: f64) -> Bson {
149        Bson::Double(a)
150    }
151}
152
153impl From<&str> for Bson {
154    fn from(s: &str) -> Bson {
155        Bson::String(s.to_owned())
156    }
157}
158
159impl From<String> for Bson {
160    fn from(a: String) -> Bson {
161        Bson::String(a)
162    }
163}
164
165impl From<Document> for Bson {
166    fn from(a: Document) -> Bson {
167        Bson::Document(a)
168    }
169}
170
171impl From<bool> for Bson {
172    fn from(a: bool) -> Bson {
173        Bson::Boolean(a)
174    }
175}
176
177impl From<Binary> for Bson {
178    fn from(binary: Binary) -> Bson {
179        Bson::Binary(binary)
180    }
181}
182
183impl From<Timestamp> for Bson {
184    fn from(ts: Timestamp) -> Bson {
185        Bson::Timestamp(ts)
186    }
187}
188
189impl<T> From<&T> for Bson
190where
191    T: Clone + Into<Bson>,
192{
193    fn from(t: &T) -> Bson {
194        t.clone().into()
195    }
196}
197
198impl<T> From<Vec<T>> for Bson
199where
200    T: Into<Bson>,
201{
202    fn from(v: Vec<T>) -> Bson {
203        Bson::Array(v.into_iter().map(|val| val.into()).collect())
204    }
205}
206
207impl<T> From<&[T]> for Bson
208where
209    T: Clone + Into<Bson>,
210{
211    fn from(s: &[T]) -> Bson {
212        Bson::Array(s.iter().cloned().map(|val| val.into()).collect())
213    }
214}
215
216impl<T: Into<Bson>> ::std::iter::FromIterator<T> for Bson {
217    /// # Examples
218    ///
219    /// ```
220    /// use std::iter::FromIterator;
221    /// use bson::Bson;
222    ///
223    /// let x: Bson = Bson::from_iter(vec!["lorem", "ipsum", "dolor"]);
224    /// // or
225    /// let x: Bson = vec!["lorem", "ipsum", "dolor"].into_iter().collect();
226    /// ```
227    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
228        Bson::Array(iter.into_iter().map(Into::into).collect())
229    }
230}
231
232impl From<i32> for Bson {
233    fn from(a: i32) -> Bson {
234        Bson::Int32(a)
235    }
236}
237
238impl From<i64> for Bson {
239    fn from(a: i64) -> Bson {
240        Bson::Int64(a)
241    }
242}
243
244impl From<u32> for Bson {
245    fn from(a: u32) -> Bson {
246        Bson::UInt32(a)
247    }
248}
249
250impl From<u64> for Bson {
251    fn from(a: u64) -> Bson {
252        Bson::UInt64(a)
253    }
254}
255
256
257#[cfg(feature = "chrono-0_4")]
258#[cfg_attr(docsrs, doc(cfg(feature = "chrono-0_4")))]
259impl<T: chrono::TimeZone> From<chrono::DateTime<T>> for Bson {
260    fn from(a: chrono::DateTime<T>) -> Bson {
261        Bson::DateTime(crate::DateTime::from(a))
262    }
263}
264
265#[cfg(feature = "uuid-0_8")]
266#[cfg_attr(docsrs, doc(cfg(feature = "uuid-0_8")))]
267impl From<uuid::Uuid> for Bson {
268    fn from(uuid: uuid::Uuid) -> Self {
269        Bson::Binary(uuid.into())
270    }
271}
272
273impl From<crate::DateTime> for Bson {
274    fn from(dt: crate::DateTime) -> Self {
275        Bson::DateTime(dt)
276    }
277}
278
279impl<T> From<Option<T>> for Bson
280where
281    T: Into<Bson>,
282{
283    fn from(a: Option<T>) -> Bson {
284        match a {
285            None => Bson::Null,
286            Some(t) => t.into(),
287        }
288    }
289}
290
291/// This will create the [relaxed Extended JSON v2](https://docs.mongodb.com/manual/reference/mongodb-extended-json/) representation of the provided [`Bson`](../enum.Bson.html).
292impl From<Bson> for Value {
293    fn from(bson: Bson) -> Self {
294        bson.into_relaxed_extjson()
295    }
296}
297
298impl Bson {
299    /// Converts the Bson value into its [relaxed extended JSON representation](https://docs.mongodb.com/manual/reference/mongodb-extended-json/).
300    ///
301    /// Note: If this method is called on a case which contains a `Decimal128` value, it will panic.
302    pub fn into_relaxed_extjson(self) -> Value {
303        match self {
304            Bson::Double(v) if v.is_nan() => {
305                let s = if v.is_sign_negative() { "-NaN" } else { "NaN" };
306
307                json!({ "$numberDouble": s })
308            }
309            Bson::Double(v) if v.is_infinite() => {
310                let s = if v.is_sign_negative() {
311                    "-Infinity"
312                } else {
313                    "Infinity"
314                };
315
316                json!({ "$numberDouble": s })
317            }
318            Bson::Double(v) => json!(v),
319            Bson::String(v) => json!(v),
320            Bson::Array(v) => json!(v),
321            Bson::Document(v) => {
322                Value::Object(v.into_iter().map(|(k, v)| (k, Value::from(v))).collect())
323            }
324            Bson::Boolean(v) => json!(v),
325            Bson::Null => Value::Null,
326            Bson::Int32(v) => v.into(),
327            Bson::Int64(v) => v.into(),
328            Bson::UInt32(v) => v.into(),
329            Bson::UInt64(v) => v.into(),
330            Bson::Timestamp(Timestamp { time, increment }) => json!({
331                "$timestamp": {
332                    "t": time,
333                    "i": increment,
334                }
335            }),
336            Bson::Binary(Binary { subtype, ref bytes }) => {
337                let tval: u8 = From::from(subtype);
338                json!({
339                    "$binary": {
340                        "base64": base64::encode(bytes),
341                        "subType": hex::encode([tval]),
342                    }
343                })
344            }
345            Bson::DateTime(v) if v.timestamp_millis() >= 0 && v.to_chrono().year() <= 99999 => {
346                json!({
347                    "$date": v.to_rfc3339_string(),
348                })
349            }
350            Bson::DateTime(v) => json!({
351                "$date": { "$numberLong": v.timestamp_millis().to_string() },
352            }),
353            Bson::Decimal128(_) => panic!("Decimal128 extended JSON not implemented yet."),
354        }
355    }
356
357    /// Converts the Bson value into its [canonical extended JSON representation](https://docs.mongodb.com/manual/reference/mongodb-extended-json/).
358    ///
359    /// Note: extended json encoding for `Decimal128` values is not supported. If this method is
360    /// called on a case which contains a `Decimal128` value, it will panic.
361    pub fn into_canonical_extjson(self) -> Value {
362        match self {
363            Bson::Int32(i) => json!({ "$numberInt": i.to_string() }),
364            Bson::Int64(i) => json!({ "$numberLong": i.to_string() }),
365            Bson::UInt32(i) => json!({ "$numberUInt32": i.to_string() }),
366            Bson::UInt64(i) => json!({ "$numberUInt64": i.to_string() }),
367            Bson::Double(f) if f.is_normal() => {
368                let mut s = f.to_string();
369                if f.fract() == 0.0 {
370                    s.push_str(".0");
371                }
372
373                json!({ "$numberDouble": s })
374            }
375            Bson::Double(f) if f == 0.0 => {
376                let s = if f.is_sign_negative() { "-0.0" } else { "0.0" };
377
378                json!({ "$numberDouble": s })
379            }
380            Bson::DateTime(date) => {
381                json!({ "$date": { "$numberLong": date.timestamp_millis().to_string() } })
382            }
383            Bson::Array(arr) => {
384                Value::Array(arr.into_iter().map(Bson::into_canonical_extjson).collect())
385            }
386            Bson::Document(arr) => Value::Object(
387                arr.into_iter()
388                    .map(|(k, v)| (k, v.into_canonical_extjson()))
389                    .collect(),
390            ),
391            other => other.into_relaxed_extjson(),
392        }
393    }
394
395    /// Get the `ElementType` of this value.
396    pub fn element_type(&self) -> ElementType {
397        match *self {
398            Bson::Double(..) => ElementType::Double,
399            Bson::String(..) => ElementType::String,
400            Bson::Array(..) => ElementType::Array,
401            Bson::Document(..) => ElementType::EmbeddedDocument,
402            Bson::Boolean(..) => ElementType::Boolean,
403            Bson::Null => ElementType::Null,
404            Bson::Int32(..) => ElementType::Int32,
405            Bson::Int64(..) => ElementType::Int64,
406            Bson::UInt32(..) => ElementType::UInt32,
407            Bson::UInt64(..) => ElementType::UInt64,
408            Bson::Timestamp(..) => ElementType::Timestamp,
409            Bson::Binary(..) => ElementType::Binary,
410            Bson::DateTime(..) => ElementType::DateTime,
411            Bson::Decimal128(..) => ElementType::Decimal128,
412        }
413    }
414
415    /// Converts to extended format.
416    /// This function mainly used for [extended JSON format](https://docs.mongodb.com/manual/reference/mongodb-extended-json/).
417    // TODO RUST-426: Investigate either removing this from the serde implementation or unifying
418    // with the extended JSON implementation.
419    pub(crate) fn into_extended_document(self) -> Document {
420        match self {
421            Bson::Timestamp(Timestamp { time, increment }) => {
422                doc! {
423                    "$timestamp": {
424                        "t": time,
425                        "i": increment,
426                    }
427                }
428            }
429            Bson::Binary(Binary { subtype, ref bytes }) => {
430                let tval: u8 = From::from(subtype);
431                doc! {
432                    "$binary": {
433                        "base64": base64::encode(bytes),
434                        "subType": hex::encode([tval]),
435                    }
436                }
437            }
438            Bson::DateTime(v) if v.timestamp_millis() >= 0 && v.to_chrono().year() <= 9999 => {
439                doc! {
440                    "$date": v.to_rfc3339_string(),
441                }
442            }
443            Bson::DateTime(v) => doc! {
444                "$date": { "$numberLong": v.timestamp_millis().to_string() },
445            },
446            _ => panic!("Attempted conversion of invalid data type: {}", self),
447        }
448    }
449
450    pub(crate) fn from_extended_document(doc: Document) -> Bson {
451        if doc.len() > 2 {
452            return Bson::Document(doc);
453        }
454
455        let mut keys: Vec<_> = doc.keys().map(|s| s.as_str()).collect();
456        keys.sort_unstable();
457
458        match keys.as_slice() {
459            ["$numberInt"] => {
460                if let Ok(i) = doc.get_str("$numberInt") {
461                    if let Ok(i) = i.parse() {
462                        return Bson::Int32(i);
463                    }
464                }
465            }
466
467            ["$numberLong"] => {
468                if let Ok(i) = doc.get_str("$numberLong") {
469                    if let Ok(i) = i.parse() {
470                        return Bson::Int64(i);
471                    }
472                }
473            }
474
475            ["$numberDouble"] => match doc.get_str("$numberDouble") {
476                Ok("Infinity") => return Bson::Double(std::f64::INFINITY),
477                Ok("-Infinity") => return Bson::Double(std::f64::NEG_INFINITY),
478                Ok("NaN") => return Bson::Double(std::f64::NAN),
479                Ok(other) => {
480                    if let Ok(d) = other.parse() {
481                        return Bson::Double(d);
482                    }
483                }
484                _ => {}
485            },
486
487            ["$numberDecimalBytes"] => {
488                if let Ok(bytes) = doc.get_binary_generic("$numberDecimalBytes") {
489                    if let Ok(b) = bytes.clone().try_into() {
490                        return Bson::Decimal128(Decimal128 { bytes: b });
491                    }
492                }
493            }
494
495            ["$binary"] => {
496                if let Some(binary) = Binary::from_extended_doc(&doc) {
497                    return Bson::Binary(binary);
498                }
499            }
500
501            ["$timestamp"] => {
502                if let Ok(timestamp) = doc.get_document("$timestamp") {
503                    if let Ok(t) = timestamp.get_i32("t") {
504                        if let Ok(i) = timestamp.get_i32("i") {
505                            return Bson::Timestamp(Timestamp {
506                                time: t as u32,
507                                increment: i as u32,
508                            });
509                        }
510                    }
511
512                    if let Ok(t) = timestamp.get_i64("t") {
513                        if let Ok(i) = timestamp.get_i64("i") {
514                            if t >= 0
515                                && i >= 0
516                                && t <= (std::u32::MAX as i64)
517                                && i <= (std::u32::MAX as i64)
518                            {
519                                return Bson::Timestamp(Timestamp {
520                                    time: t as u32,
521                                    increment: i as u32,
522                                });
523                            }
524                        }
525                    }
526                }
527            }
528
529            ["$date"] => {
530                if let Ok(date) = doc.get_i64("$date") {
531                    return Bson::DateTime(crate::DateTime::from_millis(date));
532                }
533
534                if let Ok(date) = doc.get_str("$date") {
535                    if let Ok(date) = chrono::DateTime::parse_from_rfc3339(date) {
536                        return Bson::DateTime(crate::DateTime::from_chrono(date));
537                    }
538                }
539            }
540
541            _ => {}
542        };
543
544        Bson::Document(
545            doc.into_iter()
546                .map(|(k, v)| {
547                    let v = match v {
548                        Bson::Document(v) => Bson::from_extended_document(v),
549                        other => other,
550                    };
551
552                    (k, v)
553                })
554                .collect(),
555        )
556    }
557}
558
559/// Value helpers
560impl Bson {
561    /// If `Bson` is `Double`, return its value as an `f64`. Returns `None` otherwise
562    pub fn as_f64(&self) -> Option<f64> {
563        match *self {
564            Bson::Double(v) => Some(v),
565            _ => None,
566        }
567    }
568
569    /// If `Bson` is `String`, return its value as a `&str`. Returns `None` otherwise
570    pub fn as_str(&self) -> Option<&str> {
571        match *self {
572            Bson::String(ref s) => Some(s),
573            _ => None,
574        }
575    }
576
577    /// If `Bson` is `String`, return a mutable reference to its value as a `str`. Returns `None`
578    /// otherwise
579    pub fn as_str_mut(&mut self) -> Option<&mut str> {
580        match *self {
581            Bson::String(ref mut s) => Some(s),
582            _ => None,
583        }
584    }
585
586    /// If `Bson` is `Array`, return its value. Returns `None` otherwise
587    pub fn as_array(&self) -> Option<&Array> {
588        match *self {
589            Bson::Array(ref v) => Some(v),
590            _ => None,
591        }
592    }
593
594    /// If `Bson` is `Array`, return a mutable reference to its value. Returns `None` otherwise
595    pub fn as_array_mut(&mut self) -> Option<&mut Array> {
596        match *self {
597            Bson::Array(ref mut v) => Some(v),
598            _ => None,
599        }
600    }
601
602    /// If `Bson` is `Document`, return its value. Returns `None` otherwise
603    pub fn as_document(&self) -> Option<&Document> {
604        match *self {
605            Bson::Document(ref v) => Some(v),
606            _ => None,
607        }
608    }
609
610    /// If `Bson` is `Document`, return a mutable reference to its value. Returns `None` otherwise
611    pub fn as_document_mut(&mut self) -> Option<&mut Document> {
612        match *self {
613            Bson::Document(ref mut v) => Some(v),
614            _ => None,
615        }
616    }
617
618    /// If `Bson` is `Bool`, return its value. Returns `None` otherwise
619    pub fn as_bool(&self) -> Option<bool> {
620        match *self {
621            Bson::Boolean(v) => Some(v),
622            _ => None,
623        }
624    }
625
626    /// If `Bson` is `I32`, return its value. Returns `None` otherwise
627    pub fn as_i32(&self) -> Option<i32> {
628        match *self {
629            Bson::Int32(v) => Some(v),
630            _ => None,
631        }
632    }
633
634    /// If `Bson` is `I64`, return its value. Returns `None` otherwise
635    pub fn as_i64(&self) -> Option<i64> {
636        match *self {
637            Bson::Int64(v) => Some(v),
638            _ => None,
639        }
640    }
641
642    /// If `Bson` is `U32`, return its value. Returns `None` otherwise
643    pub fn as_u32(&self) -> Option<u32> {
644        match *self {
645            Bson::UInt32(v) => Some(v),
646            _ => None,
647        }
648    }
649
650    /// If `Bson` is `U64`, return its value. Returns `None` otherwise
651    pub fn as_u64(&self) -> Option<u64> {
652        match *self {
653            Bson::UInt64(v) => Some(v),
654            _ => None,
655        }
656    }
657
658    /// If `Bson` is `DateTime`, return its value. Returns `None` otherwise
659    pub fn as_datetime(&self) -> Option<&crate::DateTime> {
660        match *self {
661            Bson::DateTime(ref v) => Some(v),
662            _ => None,
663        }
664    }
665
666    /// If `Bson` is `DateTime`, return a mutable reference to its value. Returns `None`
667    /// otherwise
668    pub fn as_datetime_mut(&mut self) -> Option<&mut crate::DateTime> {
669        match *self {
670            Bson::DateTime(ref mut v) => Some(v),
671            _ => None,
672        }
673    }
674
675    /// If `Bson` is `Timestamp`, return its value. Returns `None` otherwise
676    pub fn as_timestamp(&self) -> Option<Timestamp> {
677        match *self {
678            Bson::Timestamp(timestamp) => Some(timestamp),
679            _ => None,
680        }
681    }
682
683    /// If `Bson` is `Null`, return its value. Returns `None` otherwise
684    pub fn as_null(&self) -> Option<()> {
685        match *self {
686            Bson::Null => Some(()),
687            _ => None,
688        }
689    }
690
691}
692
693/// Represents a BSON timestamp value.
694#[derive(Debug, Eq, Ord, PartialEq, PartialOrd, Clone, Copy, Hash)]
695pub struct Timestamp {
696    /// The number of seconds since the Unix epoch.
697    pub time: u32,
698
699    /// An incrementing value to order timestamps with the same number of seconds in the `time`
700    /// field.
701    pub increment: u32,
702}
703
704impl Display for Timestamp {
705    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
706        write!(fmt, "Timestamp({}, {})", self.time, self.increment)
707    }
708}
709
710impl Timestamp {
711    pub(crate) fn to_le_i64(self) -> i64 {
712        let upper = (self.time.to_le() as u64) << 32;
713        let lower = self.increment.to_le() as u64;
714
715        (upper | lower) as i64
716    }
717
718    pub(crate) fn from_le_i64(val: i64) -> Self {
719        let ts = val.to_le();
720
721        Timestamp {
722            time: ((ts as u64) >> 32) as u32,
723            increment: (ts & 0xFFFF_FFFF) as u32,
724        }
725    }
726}
727
728
729/// Represents a BSON binary value.
730#[derive(Debug, Clone, PartialEq)]
731pub struct Binary {
732    /// The subtype of the bytes.
733    pub subtype: BinarySubtype,
734
735    /// The binary bytes.
736    pub bytes: Vec<u8>,
737}
738
739impl Display for Binary {
740    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
741        write!(
742            fmt,
743            "Binary({:#x}, {})",
744            u8::from(self.subtype),
745            base64::encode(&self.bytes)
746        )
747    }
748}
749
750impl Binary {
751    pub(crate) fn from_extended_doc(doc: &Document) -> Option<Self> {
752        let binary_doc = doc.get_document("$binary").ok()?;
753
754        if let Ok(bytes) = binary_doc.get_str("base64") {
755            let bytes = base64::decode(bytes).ok()?;
756            let subtype = binary_doc.get_str("subType").ok()?;
757            let subtype = hex::decode(subtype).ok()?;
758            if subtype.len() == 1 {
759                Some(Self {
760                    bytes,
761                    subtype: subtype[0].into(),
762                })
763            } else {
764                None
765            }
766        } else {
767            // in non-human-readable mode, RawBinary will serialize as
768            // { "$binary": { "bytes": <bytes>, "subType": <i32> } };
769            let binary = binary_doc.get_binary_generic("bytes").ok()?;
770            let subtype = binary_doc.get_i32("subType").ok()?;
771
772            Some(Self {
773                bytes: binary.clone(),
774                subtype: u8::try_from(subtype).ok()?.into(),
775            })
776        }
777    }
778}