use_prelude!();
use std::{borrow::BorrowMut, time::SystemTime};
use serde::{Deserialize, Serialize};
use crate::error::{DittoError, ErrorKind};
pub trait DittoDocument {
fn id(&self) -> DocumentId;
fn to_cbor(&self) -> Result<serde_cbor::Value, DittoError>;
fn get<V: for<'de> Deserialize<'de>>(&self, path: &str) -> Result<V, DittoError>;
fn typed<T: for<'de> Deserialize<'de>>(&self) -> Result<T, DittoError>;
}
pub trait DittoMutDocument: DittoDocument {
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 push<V: Serialize>(&mut self, path: &str, val: V) -> Result<(), DittoError>;
fn insert<V: Serialize>(&mut self, path: &str, val: V) -> Result<(), DittoError>;
fn pop<T>(&mut self, path: &str) -> Result<T, DittoError>
where
T: for<'de> Deserialize<'de>;
fn remove(&mut self, path: &str) -> Result<(), DittoError>;
fn replace_with_counter(&mut self, path: &str, is_default: bool) -> 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: for<'de> Deserialize<'de>>(&self, key: &str) -> Result<V, DittoError> {
let c_key = char_p::new(key);
let c_val = unsafe { ffi_sdk::ditto_document_get_cbor(self.borrow(), c_key.as_ref()) };
c_val
.ok_or_else(|| DittoError::from(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
})
}
fn typed<V: for<'de> Deserialize<'de>>(&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
}
}
impl<T: BorrowMut<ffi_sdk::Document>> DittoMutDocument for T {
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 insert<V: Serialize>(&mut self, path: &str, value: V) -> Result<(), DittoError> {
let c_str = char_p::new(path);
let cbor: Vec<u8> =
serde_cbor::to_vec(&value).map_err(|e| DittoError::new(ErrorKind::InvalidInput, e))?;
let status = unsafe {
ffi_sdk::ditto_document_insert_cbor(
self.borrow_mut(),
c_str.as_ref(),
cbor.as_slice().into(),
)
};
if status != 0 {
Err(DittoError::from_ffi(ErrorKind::InvalidInput))
} else {
Ok(())
}
}
fn push<V: Serialize>(&mut self, path: &str, value: V) -> Result<(), DittoError> {
let c_str = char_p::new(path);
let cbor: Vec<u8> =
serde_cbor::to_vec(&value).map_err(|e| DittoError::new(ErrorKind::InvalidInput, e))?;
let status = unsafe {
ffi_sdk::ditto_document_push_cbor(
self.borrow_mut(),
c_str.as_ref(),
cbor.as_slice().into(),
)
};
if status != 0 {
Err(DittoError::from_ffi(ErrorKind::InvalidInput))
} else {
Ok(())
}
}
fn pop<V>(&mut self, path: &str) -> Result<V, DittoError>
where
V: for<'de> Deserialize<'de>,
{
let c_str = char_p::new(path);
let cbor = {
use ::safer_ffi::prelude::{AsOut, ManuallyDropMut};
let mut slot: c_slice::Box<u8> = Default::default();
let out_cbor: Out<c_slice::Box<u8>> = slot.manually_drop_mut().as_out();
let status = unsafe {
ffi_sdk::ditto_document_pop_cbor(self.borrow_mut(), c_str.as_ref(), out_cbor)
};
if status != 0 {
return Err(DittoError::from_ffi(ErrorKind::InvalidInput));
}
slot
};
let result = serde_cbor::from_slice::<V>(cbor.as_slice())
.map_err(|e| DittoError::new(ErrorKind::InvalidInput, e));
unsafe {
ffi_sdk::ditto_c_bytes_free(cbor);
}
result
}
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(())
}
}
fn replace_with_counter(&mut self, path: &str, is_default: bool) -> Result<(), DittoError> {
let pointer = char_p::new(path);
let status = if is_default {
let timestamp = match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
Ok(n) => n.as_secs(),
Err(e) => return Err(DittoError::new(ErrorKind::Internal, e)),
};
unsafe {
ffi_sdk::ditto_document_replace_with_counter_with_timestamp(
self.borrow_mut(),
pointer.as_ref(),
timestamp as u32,
)
}
} else {
unsafe {
ffi_sdk::ditto_document_replace_with_counter(self.borrow_mut(), pointer.as_ref())
}
};
if status != 0 {
Err(DittoError::from_ffi(ErrorKind::InvalidInput))
} else {
Ok(())
}
}
}