use std::{
convert::TryInto,
ops::{Deref, DerefMut},
};
use serde::{de::DeserializeOwned, Serialize};
pub use serde_cbor::Value;
use super::{Document, Primary, Result, ResultWrap};
#[derive(Debug, Clone, PartialEq)]
pub struct RawDocument(Option<Primary>, Value);
impl RawDocument {
#[inline]
pub fn new(doc: Value) -> Self {
RawDocument(None, doc)
}
#[inline]
pub fn with_id(mut self, id: Primary) -> Self {
self.0 = Some(id);
self
}
#[inline]
pub fn without_id(mut self) -> Self {
self.0 = None;
self
}
#[inline]
pub fn has_id(&self) -> bool {
self.0.is_some()
}
#[inline]
pub fn get_id(&self) -> &Option<Primary> {
&self.0
}
#[inline]
pub fn req_id(&self) -> Result<Primary> {
self.get_id()
.ok_or_else(|| "Missing document id")
.wrap_err()
}
#[inline]
pub fn into_inner(self) -> Value {
self.1
}
pub fn to_bin(&self) -> Result<Vec<u8>> {
serde_cbor::to_vec(&self.1).wrap_err()
}
pub fn from_bin(raw: &[u8]) -> Result<Self> {
serde_cbor::from_slice(raw).map(Self::new).wrap_err()
}
pub fn from_doc<T>(doc: &T) -> Result<Self>
where
T: Serialize + Document + Sized,
{
let mut raw = to_value(doc)?;
let id = if let Value::Map(ref mut obj) = &mut raw {
obj.remove(&Value::Text(T::primary_field().as_ref().into()))
} else {
return Err("Document must be represented as an object").wrap_err();
};
let id = match id {
None => None,
Some(Value::Null) => None,
Some(Value::Integer(id)) => Some(id as u32),
_ => return Err("Document primary must be an integer").wrap_err(),
};
Ok(RawDocument(id, raw))
}
pub fn into_doc<T>(self) -> Result<T>
where
T: DeserializeOwned + Document,
{
let RawDocument(id, mut raw) = self;
if let Value::Map(ref mut obj) = &mut raw {
if let Some(id) = &id {
obj.insert(
Value::Text(T::primary_field().as_ref().into()),
Value::Integer(u64::from(*id).try_into().unwrap()),
);
}
} else {
return Err("Document must be represented as an object").wrap_err();
}
serde_cbor::value::from_value(raw).wrap_err()
}
}
impl Deref for RawDocument {
type Target = Value;
fn deref(&self) -> &Self::Target {
&self.1
}
}
impl DerefMut for RawDocument {
fn deref_mut(&mut self) -> &mut Value {
&mut self.1
}
}
#[inline]
pub fn to_value<T: Serialize>(value: T) -> Result<Value> {
serde_cbor::value::to_value(value).wrap_err()
}
#[cfg(test)]
mod test {
use super::super::{Document, Identifier, Primary, RawDocument};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct User {
id: Option<Primary>,
name: String,
email: Option<String>,
}
impl Document for User {
fn primary_field() -> Identifier {
"id".into()
}
}
#[test]
fn raw_doc() {
let src = User {
id: None,
name: "elen".into(),
email: None,
};
let raw = RawDocument::from_doc(&src).unwrap();
let res = raw.clone().into_doc::<User>().unwrap();
assert_eq!(res, src);
assert_eq!(RawDocument::from_doc(&res).unwrap(), raw);
}
#[test]
fn gen_doc() {
let src = User {
id: None,
name: "elen".into(),
email: None,
};
let bin = RawDocument::from_doc(&src).unwrap().to_bin().unwrap();
let raw = RawDocument::from_bin(&bin).unwrap();
let res = RawDocument::from_doc(&src).unwrap();
let doc = res.clone().into_doc::<User>().unwrap();
assert_eq!(res, raw);
assert_eq!(doc, src);
assert_eq!(res.to_bin().unwrap(), raw.to_bin().unwrap());
}
#[test]
#[ignore]
fn duplicate_id() {
let src = User {
id: Some(1),
name: "ivan".into(),
email: None,
};
let res = RawDocument::from_doc(&src).unwrap().into_doc::<User>();
assert!(res.is_err());
}
}