use std::{
borrow::Cow,
convert::{TryFrom, TryInto},
};
use serde::{ser::SerializeMap, Deserialize, Serialize};
use crate::{
raw::{error::ErrorKind, RawBsonVisitor, RAW_DOCUMENT_NEWTYPE},
spec::BinarySubtype,
DateTime,
Timestamp,
};
use super::{
error::{ValueAccessError, ValueAccessErrorKind, ValueAccessResult},
i32_from_slice,
Error,
Iter,
RawArray,
RawBinary,
RawBson,
RawDocumentBuf,
RawRegex,
Result,
};
use crate::{oid::ObjectId, spec::ElementType, Document};
#[derive(PartialEq)]
#[repr(transparent)]
pub struct RawDocument {
data: [u8],
}
impl RawDocument {
pub fn new<D: AsRef<[u8]> + ?Sized>(data: &D) -> Result<&RawDocument> {
let data = data.as_ref();
if data.len() < 5 {
return Err(Error {
key: None,
kind: ErrorKind::MalformedValue {
message: "document too short".into(),
},
});
}
let length = i32_from_slice(data)?;
if data.len() as i32 != length {
return Err(Error {
key: None,
kind: ErrorKind::MalformedValue {
message: "document length incorrect".into(),
},
});
}
if data[data.len() - 1] != 0 {
return Err(Error {
key: None,
kind: ErrorKind::MalformedValue {
message: "document not null-terminated".into(),
},
});
}
Ok(RawDocument::new_unchecked(data))
}
pub(crate) fn new_unchecked<D: AsRef<[u8]> + ?Sized>(data: &D) -> &RawDocument {
unsafe { &*(data.as_ref() as *const [u8] as *const RawDocument) }
}
pub fn to_raw_document_buf(&self) -> RawDocumentBuf {
RawDocumentBuf::new(self.data.to_owned()).unwrap()
}
pub fn get(&self, key: impl AsRef<str>) -> Result<Option<RawBson<'_>>> {
for result in self.into_iter() {
let (k, v) = result?;
if key.as_ref() == k {
return Ok(Some(v));
}
}
Ok(None)
}
fn get_with<'a, T>(
&'a self,
key: impl AsRef<str>,
expected_type: ElementType,
f: impl FnOnce(RawBson<'a>) -> Option<T>,
) -> ValueAccessResult<T> {
let key = key.as_ref();
let bson = self
.get(key)
.map_err(|e| ValueAccessError {
key: key.to_string(),
kind: ValueAccessErrorKind::InvalidBson(e),
})?
.ok_or(ValueAccessError {
key: key.to_string(),
kind: ValueAccessErrorKind::NotPresent,
})?;
match f(bson) {
Some(t) => Ok(t),
None => Err(ValueAccessError {
key: key.to_string(),
kind: ValueAccessErrorKind::UnexpectedType {
expected: expected_type,
actual: bson.element_type(),
},
}),
}
}
pub fn get_f64(&self, key: impl AsRef<str>) -> ValueAccessResult<f64> {
self.get_with(key, ElementType::Double, RawBson::as_f64)
}
pub fn get_str(&self, key: impl AsRef<str>) -> ValueAccessResult<&'_ str> {
self.get_with(key, ElementType::String, RawBson::as_str)
}
pub fn get_document(&self, key: impl AsRef<str>) -> ValueAccessResult<&'_ RawDocument> {
self.get_with(key, ElementType::EmbeddedDocument, RawBson::as_document)
}
pub fn get_array(&self, key: impl AsRef<str>) -> ValueAccessResult<&'_ RawArray> {
self.get_with(key, ElementType::Array, RawBson::as_array)
}
pub fn get_binary(&self, key: impl AsRef<str>) -> ValueAccessResult<RawBinary<'_>> {
self.get_with(key, ElementType::Binary, RawBson::as_binary)
}
pub fn get_object_id(&self, key: impl AsRef<str>) -> ValueAccessResult<ObjectId> {
self.get_with(key, ElementType::ObjectId, RawBson::as_object_id)
}
pub fn get_bool(&self, key: impl AsRef<str>) -> ValueAccessResult<bool> {
self.get_with(key, ElementType::Boolean, RawBson::as_bool)
}
pub fn get_datetime(&self, key: impl AsRef<str>) -> ValueAccessResult<DateTime> {
self.get_with(key, ElementType::DateTime, RawBson::as_datetime)
}
pub fn get_regex(&self, key: impl AsRef<str>) -> ValueAccessResult<RawRegex<'_>> {
self.get_with(key, ElementType::RegularExpression, RawBson::as_regex)
}
pub fn get_timestamp(&self, key: impl AsRef<str>) -> ValueAccessResult<Timestamp> {
self.get_with(key, ElementType::Timestamp, RawBson::as_timestamp)
}
pub fn get_i32(&self, key: impl AsRef<str>) -> ValueAccessResult<i32> {
self.get_with(key, ElementType::Int32, RawBson::as_i32)
}
pub fn get_i64(&self, key: impl AsRef<str>) -> ValueAccessResult<i64> {
self.get_with(key, ElementType::Int64, RawBson::as_i64)
}
pub fn as_bytes(&self) -> &[u8] {
&self.data
}
}
impl<'de: 'a, 'a> Deserialize<'de> for &'a RawDocument {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
match deserializer.deserialize_newtype_struct(RAW_DOCUMENT_NEWTYPE, RawBsonVisitor)? {
RawBson::Document(d) => Ok(d),
RawBson::Binary(b) if b.subtype == BinarySubtype::Generic => {
RawDocument::new(b.bytes).map_err(serde::de::Error::custom)
}
o => Err(serde::de::Error::custom(format!(
"expected raw document reference, instead got {:?}",
o
))),
}
}
}
impl<'a> Serialize for &'a RawDocument {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
struct KvpSerializer<'a>(&'a RawDocument);
impl<'a> Serialize for KvpSerializer<'a> {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if serializer.is_human_readable() {
let mut map = serializer.serialize_map(None)?;
for kvp in self.0 {
let (k, v) = kvp.map_err(serde::ser::Error::custom)?;
map.serialize_entry(k, &v)?;
}
map.end()
} else {
serializer.serialize_bytes(self.0.as_bytes())
}
}
}
serializer.serialize_newtype_struct(RAW_DOCUMENT_NEWTYPE, &KvpSerializer(self))
}
}
impl std::fmt::Debug for RawDocument {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RawDocument")
.field("data", &hex::encode(&self.data))
.finish()
}
}
impl AsRef<RawDocument> for RawDocument {
fn as_ref(&self) -> &RawDocument {
self
}
}
impl ToOwned for RawDocument {
type Owned = RawDocumentBuf;
fn to_owned(&self) -> Self::Owned {
self.to_raw_document_buf()
}
}
impl<'a> From<&'a RawDocument> for Cow<'a, RawDocument> {
fn from(rdr: &'a RawDocument) -> Self {
Cow::Borrowed(rdr)
}
}
impl TryFrom<&RawDocument> for crate::Document {
type Error = Error;
fn try_from(rawdoc: &RawDocument) -> Result<Document> {
rawdoc
.into_iter()
.map(|res| res.and_then(|(k, v)| Ok((k.to_owned(), v.try_into()?))))
.collect()
}
}
impl<'a> IntoIterator for &'a RawDocument {
type IntoIter = Iter<'a>;
type Item = Result<(&'a str, RawBson<'a>)>;
fn into_iter(self) -> Iter<'a> {
Iter::new(self)
}
}