use_prelude!();
use ffi_sdk::{COrderByParam, FsComponent, WriteStrategyRs};
use crate::error::{DittoError, ErrorKind};
pub mod batch;
pub mod collection;
pub mod collections;
pub mod ditto_attachment;
pub mod ditto_attachment_fetch_event;
pub mod ditto_attachment_fetcher;
pub mod ditto_attachment_token;
pub mod live_query;
#[cfg(feature = "timeseries")]
pub mod timeseries;
pub mod update;
use collections::pending_collections_operation::PendingCollectionsOperation;
#[derive(Clone)]
pub struct Store {
ditto: Arc<ffi_sdk::BoxedDitto>,
}
impl Store {
pub fn new(ditto: Arc<ffi_sdk::BoxedDitto>) -> Self {
Self { ditto }
}
fn validate_collection_name(name: &str) -> Result<(), DittoError> {
let mut result = Ok(());
if name.is_empty() {
result = Err(DittoError::new(
ErrorKind::InvalidInput,
String::from("Collection name can not be empty"),
));
}
if name.split_whitespace().next().is_none() {
result = Err(DittoError::new(
ErrorKind::InvalidInput,
String::from("Collection name can not only contain whitespace"),
));
}
result
}
pub fn collection(&self, collection_name: &'_ str) -> Result<Collection, DittoError> {
Self::validate_collection_name(collection_name)?;
let c_name = char_p::new(collection_name);
let status = unsafe { ffi_sdk::ditto_collection(&*self.ditto, c_name.as_ref()) };
if status != 0 {
return Err(DittoError::from_ffi(ErrorKind::InvalidInput));
}
Ok(Collection {
ditto: self.ditto.retain(),
collection_name: c_name,
})
}
pub fn collections(&self) -> PendingCollectionsOperation {
PendingCollectionsOperation::new(self.ditto.retain())
}
pub fn with_batched_write<F>(
&self,
f: F,
) -> Result<Vec<batch::WriteTransactionResult>, DittoError>
where
for<'batch> F: FnOnce(batch::ScopedStore<'batch>) -> batch::Action<'batch>,
{
batch::with_batched_write(self, f)
}
pub fn collection_names(&self) -> Result<Vec<String>, DittoError> {
let c_collections = unsafe { ffi_sdk::ditto_get_collection_names(&*self.ditto).ok()? };
Ok(c_collections
.iter()
.map(|x: &char_p::Box| -> String { x.clone().into_string() })
.collect())
}
pub fn queries_hash(&self, live_queries: &[LiveQuery]) -> Result<u64, DittoError> {
let (coll_names, queries): (Vec<_>, Vec<_>) = live_queries
.iter()
.map(|lq| (lq.collection_name.as_ref(), lq.query.as_ref()))
.unzip();
unsafe {
ffi_sdk::ditto_queries_hash(&self.ditto, coll_names[..].into(), queries[..].into()).ok()
}
}
pub fn queries_hash_mnemonic(&self, live_queries: &[LiveQuery]) -> Result<String, DittoError> {
let (coll_names, queries): (Vec<_>, Vec<_>) = live_queries
.iter()
.map(|lq| (lq.collection_name.as_ref(), lq.query.as_ref()))
.unzip();
unsafe {
ffi_sdk::ditto_queries_hash_mnemonic(
&self.ditto,
coll_names[..].into(),
queries[..].into(),
)
.ok()
.map(|c_str| c_str.unwrap().into_string())
}
}
pub fn start_all_live_query_webhooks(&self) -> Result<(), DittoError> {
unsafe {
let ret = ffi_sdk::ditto_live_query_webhook_start_all(&self.ditto);
if ret != 0 {
return Err(DittoError::from_ffi(ErrorKind::Internal));
}
}
Ok(())
}
pub fn start_live_query_webhook_by_id(&self, doc_id: DocumentId) -> Result<(), DittoError> {
unsafe {
let ret =
ffi_sdk::ditto_live_query_webhook_start_by_id(&self.ditto, doc_id.bytes[..].into());
if ret != 0 {
return Err(DittoError::from_ffi(ErrorKind::Internal));
}
}
Ok(())
}
pub fn register_live_query_webhook(
&self,
collection_name: &str,
query: &str,
url: &str,
) -> Result<DocumentId, DittoError> {
let c_collection_name = char_p::new(collection_name);
let c_query = char_p::new(query);
let c_url = char_p::new(url);
let order_definitions: Vec<COrderByParam> = Vec::with_capacity(0);
let doc_id = unsafe {
ffi_sdk::ditto_live_query_webhook_register_str(
&self.ditto,
c_collection_name.as_ref(),
c_query.as_ref(),
order_definitions[..].into(),
-1,
0,
c_url.as_ref(),
)
.ok()?
.to::<Box<[u8]>>()
.into()
};
Ok(doc_id)
}
pub fn live_query_webhook_generate_new_api_secret(&self) -> Result<(), DittoError> {
unsafe {
let ret = ffi_sdk::ditto_live_query_webhook_generate_new_api_secret(&self.ditto);
if ret != 0 {
return Err(DittoError::from_ffi(ErrorKind::Internal));
}
}
Ok(())
}
pub fn timeseries(&self, ts_name: &'_ str) -> Result<TimeSeries, DittoError> {
Self::validate_collection_name(ts_name)?;
let c_name = char_p::new(ts_name);
Ok(TimeSeries {
ditto: self.ditto.retain(),
ts_name: c_name,
})
}
}
impl DiskUser for Store {
fn ditto_component() -> FsComponent {
FsComponent::Store
}
fn ditto(&self) -> Arc<BoxedDitto> {
self.ditto.retain()
}
}
#[non_exhaustive]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum SortDirection {
Ascending,
Descending,
}
#[non_exhaustive]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum WriteStrategy {
Merge,
InsertIfAbsent,
InsertDefaultIfAbsent,
}
impl WriteStrategy {
fn as_write_strategy_rs(&self) -> WriteStrategyRs {
match self {
WriteStrategy::Merge => WriteStrategyRs::Merge,
WriteStrategy::InsertIfAbsent => WriteStrategyRs::InsertIfAbsent,
WriteStrategy::InsertDefaultIfAbsent => WriteStrategyRs::InsertDefaultIfAbsent,
}
}
}