use std::convert::{TryFrom, TryInto};
use serde::{Deserialize, Serialize};
use crate::{
oid::{self, ObjectId},
raw::RAW_BSON_NEWTYPE,
spec::ElementType,
Binary,
Bson,
DbPointer,
Decimal128,
RawArray,
RawArrayBuf,
RawBinaryRef,
RawBsonRef,
RawDbPointerRef,
RawDocument,
RawDocumentBuf,
RawJavaScriptCodeWithScopeRef,
RawRegexRef,
Regex,
Timestamp,
};
use super::{
serde::{bson_visitor::OwnedOrBorrowedRawBsonVisitor, OwnedOrBorrowedRawBson},
Error,
Result,
};
#[derive(Debug, Clone, PartialEq)]
pub enum RawBson {
Double(f64),
String(String),
Array(RawArrayBuf),
Document(RawDocumentBuf),
Boolean(bool),
Null,
RegularExpression(Regex),
JavaScriptCode(String),
JavaScriptCodeWithScope(RawJavaScriptCodeWithScope),
Int32(i32),
Int64(i64),
Timestamp(Timestamp),
Binary(Binary),
ObjectId(oid::ObjectId),
DateTime(crate::DateTime),
Symbol(String),
Decimal128(Decimal128),
Undefined,
MaxKey,
MinKey,
DbPointer(DbPointer),
}
impl RawBson {
pub fn element_type(&self) -> ElementType {
match *self {
RawBson::Double(..) => ElementType::Double,
RawBson::String(..) => ElementType::String,
RawBson::Array(..) => ElementType::Array,
RawBson::Document(..) => ElementType::EmbeddedDocument,
RawBson::Boolean(..) => ElementType::Boolean,
RawBson::Null => ElementType::Null,
RawBson::RegularExpression(..) => ElementType::RegularExpression,
RawBson::JavaScriptCode(..) => ElementType::JavaScriptCode,
RawBson::JavaScriptCodeWithScope(..) => ElementType::JavaScriptCodeWithScope,
RawBson::Int32(..) => ElementType::Int32,
RawBson::Int64(..) => ElementType::Int64,
RawBson::Timestamp(..) => ElementType::Timestamp,
RawBson::Binary(..) => ElementType::Binary,
RawBson::ObjectId(..) => ElementType::ObjectId,
RawBson::DateTime(..) => ElementType::DateTime,
RawBson::Symbol(..) => ElementType::Symbol,
RawBson::Decimal128(..) => ElementType::Decimal128,
RawBson::Undefined => ElementType::Undefined,
RawBson::MaxKey => ElementType::MaxKey,
RawBson::MinKey => ElementType::MinKey,
RawBson::DbPointer(..) => ElementType::DbPointer,
}
}
pub fn as_f64(&self) -> Option<f64> {
match self {
RawBson::Double(d) => Some(*d),
_ => None,
}
}
pub fn as_str(&self) -> Option<&'_ str> {
match self {
RawBson::String(s) => Some(s),
_ => None,
}
}
pub fn as_array(&self) -> Option<&'_ RawArray> {
match self {
RawBson::Array(v) => Some(v),
_ => None,
}
}
pub fn as_array_mut(&mut self) -> Option<&mut RawArrayBuf> {
match self {
RawBson::Array(ref mut v) => Some(v),
_ => None,
}
}
pub fn as_document(&self) -> Option<&'_ RawDocument> {
match self {
RawBson::Document(v) => Some(v),
_ => None,
}
}
pub fn as_document_mut(&mut self) -> Option<&mut RawDocumentBuf> {
match self {
RawBson::Document(ref mut v) => Some(v),
_ => None,
}
}
pub fn as_bool(&self) -> Option<bool> {
match self {
RawBson::Boolean(v) => Some(*v),
_ => None,
}
}
pub fn as_i32(&self) -> Option<i32> {
match self {
RawBson::Int32(v) => Some(*v),
_ => None,
}
}
pub fn as_i64(&self) -> Option<i64> {
match self {
RawBson::Int64(v) => Some(*v),
_ => None,
}
}
pub fn as_object_id(&self) -> Option<oid::ObjectId> {
match self {
RawBson::ObjectId(v) => Some(*v),
_ => None,
}
}
pub fn as_binary(&self) -> Option<RawBinaryRef<'_>> {
match self {
RawBson::Binary(v) => Some(RawBinaryRef {
bytes: v.bytes.as_slice(),
subtype: v.subtype,
}),
_ => None,
}
}
pub fn as_regex(&self) -> Option<RawRegexRef<'_>> {
match self {
RawBson::RegularExpression(v) => Some(RawRegexRef {
pattern: v.pattern.as_str(),
options: v.options.as_str(),
}),
_ => None,
}
}
pub fn as_datetime(&self) -> Option<crate::DateTime> {
match self {
RawBson::DateTime(v) => Some(*v),
_ => None,
}
}
pub fn as_symbol(&self) -> Option<&'_ str> {
match self {
RawBson::Symbol(v) => Some(v),
_ => None,
}
}
pub fn as_timestamp(&self) -> Option<Timestamp> {
match self {
RawBson::Timestamp(timestamp) => Some(*timestamp),
_ => None,
}
}
pub fn as_null(&self) -> Option<()> {
match self {
RawBson::Null => Some(()),
_ => None,
}
}
pub fn as_db_pointer(&self) -> Option<RawDbPointerRef<'_>> {
match self {
RawBson::DbPointer(d) => Some(RawDbPointerRef {
namespace: d.namespace.as_str(),
id: d.id,
}),
_ => None,
}
}
pub fn as_javascript(&self) -> Option<&'_ str> {
match self {
RawBson::JavaScriptCode(s) => Some(s),
_ => None,
}
}
pub fn as_javascript_with_scope(&self) -> Option<RawJavaScriptCodeWithScopeRef<'_>> {
match self {
RawBson::JavaScriptCodeWithScope(s) => Some(RawJavaScriptCodeWithScopeRef {
code: s.code.as_str(),
scope: &s.scope,
}),
_ => None,
}
}
pub fn as_raw_bson_ref(&self) -> RawBsonRef<'_> {
match self {
RawBson::Double(d) => RawBsonRef::Double(*d),
RawBson::String(s) => RawBsonRef::String(s.as_str()),
RawBson::Array(a) => RawBsonRef::Array(a),
RawBson::Document(d) => RawBsonRef::Document(d),
RawBson::Boolean(b) => RawBsonRef::Boolean(*b),
RawBson::Null => RawBsonRef::Null,
RawBson::RegularExpression(re) => RawBsonRef::RegularExpression(RawRegexRef {
options: re.options.as_str(),
pattern: re.pattern.as_str(),
}),
RawBson::JavaScriptCode(c) => RawBsonRef::JavaScriptCode(c.as_str()),
RawBson::JavaScriptCodeWithScope(code_w_scope) => {
RawBsonRef::JavaScriptCodeWithScope(RawJavaScriptCodeWithScopeRef {
code: code_w_scope.code.as_str(),
scope: code_w_scope.scope.as_ref(),
})
}
RawBson::Int32(i) => RawBsonRef::Int32(*i),
RawBson::Int64(i) => RawBsonRef::Int64(*i),
RawBson::Timestamp(ts) => RawBsonRef::Timestamp(*ts),
RawBson::Binary(b) => RawBsonRef::Binary(RawBinaryRef {
bytes: b.bytes.as_slice(),
subtype: b.subtype,
}),
RawBson::ObjectId(oid) => RawBsonRef::ObjectId(*oid),
RawBson::DateTime(dt) => RawBsonRef::DateTime(*dt),
RawBson::Symbol(s) => RawBsonRef::Symbol(s.as_str()),
RawBson::Decimal128(d) => RawBsonRef::Decimal128(*d),
RawBson::Undefined => RawBsonRef::Undefined,
RawBson::MaxKey => RawBsonRef::MaxKey,
RawBson::MinKey => RawBsonRef::MinKey,
RawBson::DbPointer(dbp) => RawBsonRef::DbPointer(RawDbPointerRef {
namespace: dbp.namespace.as_str(),
id: dbp.id,
}),
}
}
}
impl From<i32> for RawBson {
fn from(i: i32) -> Self {
RawBson::Int32(i)
}
}
impl From<i64> for RawBson {
fn from(i: i64) -> Self {
RawBson::Int64(i)
}
}
impl From<String> for RawBson {
fn from(s: String) -> Self {
RawBson::String(s)
}
}
impl From<&str> for RawBson {
fn from(s: &str) -> Self {
RawBson::String(s.to_owned())
}
}
impl From<f64> for RawBson {
fn from(f: f64) -> Self {
RawBson::Double(f)
}
}
impl From<bool> for RawBson {
fn from(b: bool) -> Self {
RawBson::Boolean(b)
}
}
impl From<RawDocumentBuf> for RawBson {
fn from(d: RawDocumentBuf) -> Self {
RawBson::Document(d)
}
}
impl From<RawArrayBuf> for RawBson {
fn from(a: RawArrayBuf) -> Self {
RawBson::Array(a)
}
}
impl From<crate::DateTime> for RawBson {
fn from(dt: crate::DateTime) -> Self {
RawBson::DateTime(dt)
}
}
impl From<Timestamp> for RawBson {
fn from(ts: Timestamp) -> Self {
RawBson::Timestamp(ts)
}
}
impl From<ObjectId> for RawBson {
fn from(oid: ObjectId) -> Self {
RawBson::ObjectId(oid)
}
}
impl From<Decimal128> for RawBson {
fn from(d: Decimal128) -> Self {
RawBson::Decimal128(d)
}
}
impl From<RawJavaScriptCodeWithScope> for RawBson {
fn from(code_w_scope: RawJavaScriptCodeWithScope) -> Self {
RawBson::JavaScriptCodeWithScope(code_w_scope)
}
}
impl From<Binary> for RawBson {
fn from(b: Binary) -> Self {
RawBson::Binary(b)
}
}
impl From<Regex> for RawBson {
fn from(re: Regex) -> Self {
RawBson::RegularExpression(re)
}
}
impl From<DbPointer> for RawBson {
fn from(d: DbPointer) -> Self {
RawBson::DbPointer(d)
}
}
impl<'de> Deserialize<'de> for RawBson {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
match deserializer
.deserialize_newtype_struct(RAW_BSON_NEWTYPE, OwnedOrBorrowedRawBsonVisitor)?
{
OwnedOrBorrowedRawBson::Owned(o) => Ok(o),
OwnedOrBorrowedRawBson::Borrowed(b) => Ok(b.to_raw_bson()),
}
}
}
impl Serialize for RawBson {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.as_raw_bson_ref().serialize(serializer)
}
}
impl TryFrom<RawBson> for Bson {
type Error = Error;
fn try_from(rawbson: RawBson) -> Result<Bson> {
Ok(match rawbson {
RawBson::Double(d) => Bson::Double(d),
RawBson::String(s) => Bson::String(s),
RawBson::Document(rawdoc) => Bson::Document(rawdoc.as_ref().try_into()?),
RawBson::Array(rawarray) => Bson::Array(rawarray.as_ref().try_into()?),
RawBson::Binary(rawbson) => Bson::Binary(rawbson),
RawBson::ObjectId(rawbson) => Bson::ObjectId(rawbson),
RawBson::Boolean(rawbson) => Bson::Boolean(rawbson),
RawBson::DateTime(rawbson) => Bson::DateTime(rawbson),
RawBson::Null => Bson::Null,
RawBson::RegularExpression(rawregex) => Bson::RegularExpression(rawregex),
RawBson::JavaScriptCode(rawbson) => Bson::JavaScriptCode(rawbson),
RawBson::Int32(rawbson) => Bson::Int32(rawbson),
RawBson::Timestamp(rawbson) => Bson::Timestamp(rawbson),
RawBson::Int64(rawbson) => Bson::Int64(rawbson),
RawBson::Undefined => Bson::Undefined,
RawBson::DbPointer(rawbson) => Bson::DbPointer(rawbson),
RawBson::Symbol(rawbson) => Bson::Symbol(rawbson),
RawBson::JavaScriptCodeWithScope(rawbson) => {
Bson::JavaScriptCodeWithScope(crate::JavaScriptCodeWithScope {
code: rawbson.code,
scope: rawbson.scope.try_into()?,
})
}
RawBson::Decimal128(rawbson) => Bson::Decimal128(rawbson),
RawBson::MaxKey => Bson::MaxKey,
RawBson::MinKey => Bson::MinKey,
})
}
}
impl TryFrom<Bson> for RawBson {
type Error = Error;
fn try_from(bson: Bson) -> Result<RawBson> {
Ok(match bson {
Bson::Double(d) => RawBson::Double(d),
Bson::String(s) => RawBson::String(s),
Bson::Document(doc) => RawBson::Document((&doc).try_into()?),
Bson::Array(arr) => RawBson::Array(
arr.into_iter()
.map(|b| -> Result<RawBson> { b.try_into() })
.collect::<Result<RawArrayBuf>>()?,
),
Bson::Binary(bin) => RawBson::Binary(bin),
Bson::ObjectId(id) => RawBson::ObjectId(id),
Bson::Boolean(b) => RawBson::Boolean(b),
Bson::DateTime(dt) => RawBson::DateTime(dt),
Bson::Null => RawBson::Null,
Bson::RegularExpression(regex) => RawBson::RegularExpression(regex),
Bson::JavaScriptCode(s) => RawBson::JavaScriptCode(s),
Bson::Int32(i) => RawBson::Int32(i),
Bson::Timestamp(ts) => RawBson::Timestamp(ts),
Bson::Int64(i) => RawBson::Int64(i),
Bson::Undefined => RawBson::Undefined,
Bson::DbPointer(p) => RawBson::DbPointer(p),
Bson::Symbol(s) => RawBson::Symbol(s),
Bson::JavaScriptCodeWithScope(jcws) => {
RawBson::JavaScriptCodeWithScope(crate::RawJavaScriptCodeWithScope {
code: jcws.code,
scope: (&jcws.scope).try_into()?,
})
}
Bson::Decimal128(d) => RawBson::Decimal128(d),
Bson::MaxKey => RawBson::MaxKey,
Bson::MinKey => RawBson::MinKey,
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct RawJavaScriptCodeWithScope {
pub code: String,
pub scope: RawDocumentBuf,
}
impl<'de> Deserialize<'de> for RawJavaScriptCodeWithScope {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
match RawBson::deserialize(deserializer)? {
RawBson::JavaScriptCodeWithScope(b) => Ok(b),
c => Err(serde::de::Error::custom(format!(
"expected CodeWithScope, but got {:?} instead",
c
))),
}
}
}
impl Serialize for RawJavaScriptCodeWithScope {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let raw = RawJavaScriptCodeWithScopeRef {
code: self.code.as_str(),
scope: self.scope.as_ref(),
};
raw.serialize(serializer)
}
}