use core::convert::Infallible;
use core::num::TryFromIntError;
use encode::combinators::{FromError, Iter, LengthPrefix, LE};
use encode::encoders::InsufficientSpace;
use encode::{Encodable, EncodableSize};
#[derive(Debug, Clone, PartialEq)]
pub enum BsonError {
TooLarge(TryFromIntError),
InsufficientSpace(InsufficientSpace),
}
impl core::error::Error for BsonError {}
impl core::fmt::Display for BsonError {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
match self {
BsonError::TooLarge(_) => write!(f, "failed to encode BSON because it is too large"),
BsonError::InsufficientSpace(err) => core::fmt::Display::fmt(err, f),
}
}
}
impl From<TryFromIntError> for BsonError {
fn from(err: TryFromIntError) -> Self {
BsonError::TooLarge(err)
}
}
impl From<InsufficientSpace> for BsonError {
fn from(err: InsufficientSpace) -> Self {
BsonError::InsufficientSpace(err)
}
}
impl From<Infallible> for BsonError {
fn from(_: Infallible) -> Self {
unreachable!("infallible cannot be constructed")
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct BsonDocument {
pub e_list: Vec<BsonElement>,
}
impl<Encoder> Encodable<Encoder> for BsonDocument
where
Encoder: encode::ByteEncoder,
BsonError: From<Encoder::Error>,
{
type Error = BsonError;
fn encode(&self, encoder: &mut Encoder) -> Result<(), Self::Error> {
let document = (
Iter::new(&self.e_list),
FromError::<_, Self::Error>::new(0u8),
);
let size = document.encoded_size()? + (i32::BITS / 8) as usize;
LE::<i32>::try_from(size)?.encode(encoder)?;
document.encode(encoder)?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct BsonElement {
pub e_value: String,
pub variant: BsonElementVariant,
}
#[derive(Debug, Clone, PartialEq)]
pub enum BsonElementVariant {
Double(f64),
String(String),
Document(BsonDocument),
Array(BsonDocument),
Binary { subtype: u8, data: Vec<u8> },
Undefined,
ObjectId([u8; 12]),
Boolean(bool),
DateTime(i64),
Null,
Regex(String, String),
DBPointer(String, [u8; 12]),
JavaScriptCode(String),
Symbol(String),
JavaScriptCodeWithScope(String, BsonDocument),
Int32(i32),
Int64(i64),
Timestamp(u64),
Decimal128([u8; 16]), MinKey,
MaxKey,
}
impl<Encoder> Encodable<Encoder> for BsonElement
where
Encoder: encode::ByteEncoder,
BsonError: From<Encoder::Error>,
{
type Error = BsonError;
fn encode(&self, encoder: &mut Encoder) -> Result<(), Self::Error> {
let e_name = (&self.e_value, 0u8);
match &self.variant {
BsonElementVariant::Double(x) => (1i8, e_name, LE::new(*x)).encode(encoder)?,
BsonElementVariant::String(x) => {
2i8.encode(encoder)?;
e_name.encode(encoder)?;
BsonString(x).encode(encoder)?;
}
BsonElementVariant::Document(x) => {
3i8.encode(encoder)?;
e_name.encode(encoder)?;
x.encode(encoder)?;
}
BsonElementVariant::Array(x) => {
4i8.encode(encoder)?;
e_name.encode(encoder)?;
x.encode(encoder)?;
}
BsonElementVariant::Binary { subtype, data } => {
(5i8, e_name, LE::<i32>::try_from(data.len())?, subtype, data).encode(encoder)?
}
BsonElementVariant::Undefined => (6i8, e_name).encode(encoder)?,
BsonElementVariant::ObjectId(x) => (7i8, e_name, x).encode(encoder)?,
BsonElementVariant::Boolean(x) => (8i8, e_name, x).encode(encoder)?,
BsonElementVariant::DateTime(x) => (9i8, e_name, LE::new(*x)).encode(encoder)?,
BsonElementVariant::Null => (10i8, e_name).encode(encoder)?,
BsonElementVariant::Regex(x, y) => (11i8, e_name, x, 0u8, y, 0u8).encode(encoder)?,
BsonElementVariant::DBPointer(x, y) => {
(12i8, e_name).encode(encoder)?;
BsonString(x).encode(encoder)?;
y.encode(encoder)?;
}
BsonElementVariant::JavaScriptCode(x) => (13i8, e_name, x).encode(encoder)?,
BsonElementVariant::Symbol(x) => {
(14i8, e_name).encode(encoder)?;
BsonString(x).encode(encoder)?;
}
BsonElementVariant::JavaScriptCodeWithScope(x, y) => {
(15i8, e_name).encode(encoder)?;
let content = (BsonString(x), y);
let len = content.encoded_size()? + (i32::BITS / 8) as usize;
LE::<i32>::try_from(len)?.encode(encoder)?;
content.encode(encoder)?;
}
BsonElementVariant::Int32(x) => (16i8, e_name, LE::new(*x)).encode(encoder)?,
BsonElementVariant::Timestamp(x) => (17i8, e_name, LE::new(*x)).encode(encoder)?,
BsonElementVariant::Int64(x) => (18i8, e_name, LE::new(*x)).encode(encoder)?,
BsonElementVariant::Decimal128(x) => (19i8, e_name, x).encode(encoder)?,
BsonElementVariant::MinKey => (-1i8, e_name).encode(encoder)?,
BsonElementVariant::MaxKey => (127i8, e_name).encode(encoder)?,
};
Ok(())
}
}
struct BsonString<S>(S);
impl<S, Encoder> Encodable<Encoder> for BsonString<S>
where
S: AsRef<str>,
Encoder: encode::ByteEncoder,
BsonError: From<Encoder::Error>,
{
type Error = BsonError;
fn encode(&self, encoder: &mut Encoder) -> Result<(), Self::Error> {
LengthPrefix::<_, LE<i32>, BsonError>::new((self.0.as_ref(), 0u8)).encode(encoder)?;
Ok(())
}
}
fn main() -> Result<(), BsonError> {
let document = BsonDocument {
e_list: vec![
BsonElement {
e_value: "hello".into(),
variant: BsonElementVariant::Double(1.0),
},
BsonElement {
e_value: "world".into(),
variant: BsonElementVariant::String("hello".into()),
},
BsonElement {
e_value: "sub document".into(),
variant: BsonElementVariant::Document(BsonDocument {
e_list: vec![BsonElement {
e_value: "hello".into(),
variant: BsonElementVariant::Double(1.0),
}],
}),
},
BsonElement {
e_value: "array".into(),
variant: BsonElementVariant::Array(BsonDocument {
e_list: vec![BsonElement {
e_value: "0".into(), variant: BsonElementVariant::Double(1.0),
}],
}),
},
],
};
let size = document.encoded_size()?;
println!("Expected BSON size: {}", size);
let mut buf = Vec::with_capacity(size);
document.encode(&mut buf)?;
println!("{:?}", document);
println!("{:?}", buf.as_slice());
Ok(())
}
#[cfg(test)]
mod official_examples {
use super::*;
#[test]
fn assert_that_hello_world_example_is_encoded_right() {
let expected = b"\x16\x00\x00\x00\x02hello\x00\x06\x00\x00\x00world\x00\x00";
let document = BsonDocument {
e_list: vec![BsonElement {
e_value: "hello".into(),
variant: BsonElementVariant::String("world".into()),
}],
};
let mut buf = Vec::new();
document.encode(&mut buf).unwrap();
assert_eq!(buf.as_slice(), expected);
}
#[test]
fn assert_that_awesome_example_is_encoded_right() {
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";
let document = BsonDocument {
e_list: vec![BsonElement {
e_value: "BSON".into(),
variant: BsonElementVariant::Array(BsonDocument {
e_list: vec![
BsonElement {
e_value: "0".into(),
variant: BsonElementVariant::String("awesome".into()),
},
BsonElement {
e_value: "1".into(),
variant: BsonElementVariant::Double(5.05),
},
BsonElement {
e_value: "2".into(),
variant: BsonElementVariant::Int32(1986),
},
],
}),
}],
};
let mut buf = Vec::new();
document.encode(&mut buf).unwrap();
assert_eq!(buf.as_slice(), expected);
}
}