use_prelude!();
use std::{borrow::BorrowMut, time::SystemTime};
use ffi_sdk::PathAccessorType;
use serde::{de::DeserializeOwned, Serialize};
use super::type_traits::MutableValue;
use crate::error::{DittoError, ErrorKind};
pub trait DittoDocument {
fn id(&self) -> DocumentId;
fn to_cbor(&self) -> Result<serde_cbor::Value, DittoError>;
fn get<V: DeserializeOwned>(&self, path: &str) -> Result<V, DittoError>;
fn get_rga(&self, path: &str) -> Result<DittoRga, DittoError>;
fn typed<T: DeserializeOwned>(&self) -> Result<T, DittoError>;
}
pub trait DittoMutDocument: DittoDocument {
fn get_mut<V: MutableValue>(&mut self, path: &str) -> Result<V, DittoError>;
fn set<V: Serialize>(&mut self, path: &str, val: V) -> Result<(), DittoError>;
fn set_as_default<V: Serialize>(&mut self, path: &str, val: V) -> Result<(), DittoError>;
fn remove(&mut self, path: &str) -> Result<(), DittoError>;
fn increment(&mut self, path: &str, amount: f64) -> Result<(), DittoError>;
}
impl<T: Borrow<ffi_sdk::Document>> DittoDocument for T {
fn id(&self) -> DocumentId {
let id_slice = unsafe { ffi_sdk::ditto_document_id(self.borrow()) };
let id = id_slice.as_slice().to_vec().into();
unsafe {
ffi_sdk::ditto_c_bytes_free(id_slice);
}
id
}
fn to_cbor(&self) -> Result<serde_cbor::Value, DittoError> {
let c_bytes = unsafe { ffi_sdk::ditto_document_cbor(self.borrow()) };
let result = serde_cbor::from_slice(c_bytes.as_slice())
.map_err(|e| DittoError::new(ErrorKind::InvalidInput, e));
unsafe {
ffi_sdk::ditto_c_bytes_free(c_bytes);
}
result
}
fn get<V: DeserializeOwned>(&self, path: &str) -> Result<V, DittoError> {
get_internal(self.borrow(), path, PathAccessorType::Any)
}
fn get_rga(&self, path: &str) -> Result<DittoRga, DittoError> {
get_internal(self.borrow(), path, PathAccessorType::Rga)
}
fn typed<V: DeserializeOwned>(&self) -> Result<V, DittoError> {
let c_bytes = unsafe { ffi_sdk::ditto_document_cbor(self.borrow()) };
let result = serde_cbor::from_slice::<V>(c_bytes.as_slice())
.map_err(|e| DittoError::new(ErrorKind::InvalidInput, e));
unsafe {
ffi_sdk::ditto_c_bytes_free(c_bytes);
}
result
}
}
fn get_internal<V: DeserializeOwned>(
doc: &ffi_sdk::Document,
path: &str,
path_type: PathAccessorType,
) -> Result<V, DittoError> {
let c_path = char_p::new(path);
let c_val =
unsafe { ffi_sdk::ditto_document_get_cbor_with_path_type(doc, c_path.as_ref(), path_type) };
c_val
.ok_or(ErrorKind::NonExtant)
.and_then(|x| -> Result<V, _> {
let result = serde_cbor::from_slice(x.as_slice())
.map_err(|e| DittoError::new(ErrorKind::InvalidInput, e));
unsafe {
ffi_sdk::ditto_c_bytes_free(x);
}
result
})
}
impl<T: BorrowMut<ffi_sdk::Document>> DittoMutDocument for T {
fn get_mut<V: MutableValue>(&mut self, path: &str) -> Result<V> {
let base_value: V::BaseType = self.get(path)?;
let doc = self.borrow_mut();
let c_key = char_p::new(path);
V::mutable_version(base_value, doc, c_key)
}
fn set<V: Serialize>(&mut self, path: &str, val: V) -> Result<(), DittoError> {
let c_str = char_p::new(path);
let cbor: Vec<u8> =
serde_cbor::to_vec(&val).map_err(|e| DittoError::new(ErrorKind::InvalidInput, e))?;
let status = unsafe {
ffi_sdk::ditto_document_set_cbor(
self.borrow_mut(),
c_str.as_ref(),
cbor.as_slice().into(),
true, )
};
if status != 0 {
Err(DittoError::from_ffi(ErrorKind::InvalidInput))
} else {
Ok(())
}
}
fn set_as_default<V: Serialize>(&mut self, path: &str, val: V) -> Result<(), DittoError> {
let c_str = char_p::new(path);
let cbor: Vec<u8> =
serde_cbor::to_vec(&val).map_err(|e| DittoError::new(ErrorKind::InvalidInput, e))?;
let timestamp = match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
Ok(n) => n.as_secs(),
Err(e) => return Err(DittoError::new(ErrorKind::Internal, e)),
};
let status = unsafe {
ffi_sdk::ditto_document_set_cbor_with_timestamp(
self.borrow_mut(),
c_str.as_ref(),
cbor.as_slice().into(),
true, timestamp as u32,
)
};
if status != 0 {
Err(DittoError::from_ffi(ErrorKind::InvalidInput))
} else {
Ok(())
}
}
fn remove(&mut self, path: &str) -> Result<(), DittoError> {
let pointer = char_p::new(path);
let status = unsafe { ffi_sdk::ditto_document_remove(self.borrow_mut(), pointer.as_ref()) };
if status != 0 {
Err(DittoError::from_ffi(ErrorKind::InvalidInput))
} else {
Ok(())
}
}
fn increment(&mut self, path: &str, amount: f64) -> Result<(), DittoError> {
let pointer = char_p::new(path);
let status = unsafe {
ffi_sdk::ditto_document_increment_counter(self.borrow_mut(), pointer.as_ref(), amount)
};
if status != 0 {
Err(DittoError::from_ffi(ErrorKind::InvalidInput))
} else {
Ok(())
}
}
}