1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
use_prelude!();
use collection::ScopedCollection;
use super::collection::document_id::DocumentId;
mod collection;
/// Use lifetime parameters as type-level IDs (generative lifetimes),
/// so as to prevent the following anti-pattern from compiling:
///
/// ```no_run
/// use ::dittolive_ditto::prelude::*;
/// use ::std::path::Path;
/// use ::serde_json::json;
///
/// let dir1 = Path::new("...");
/// let mut ditto1 = Ditto::builder().build().unwrap();
///
/// ditto1.store().with_batched_write(|mut tx| {
/// // API requires that this closure return:
/// // - either `tx.commit_changes()`
/// // - or `tx.revert_changes()`
/// let mut coll = tx.collection("collection");
/// coll
/// .find_with_args("color == $args.color", json!({"color": "yellow"}))
/// .remove()
/// .unwrap();
/// tx.commit_changes()
/// });
/// ```
use marker::InvariantLifetime as TypeLevelId;
// Note, in practice `'batch` is `'txn`, but we hide this from the signatures to hide this
// implementation detail to the users.
pub struct ScopedStore<'batch> {
// " 'batch == 'txn "
txn: &'batch mut ffi_sdk::CWriteTransaction,
// This could be 'store (which is ≥ 'txn), but for all effects and purposes
// shrinking this to `'txn` is just as useful, and simplifies / reduces
// the number of lifetime parameters.
store: &'batch Store, // a Ref to the REAL store not the scoped store
results: &'batch mut Vec<WriteTransactionResult>,
// make sure the `'batch` lifetime is invariant so that it can be used as
// a type-level unique id.
_txn_id: TypeLevelId<'batch>,
}
pub(super) fn with_batched_write<F>(
store: &Store, // Has lifetime of 'store
f: F,
) -> Result<Vec<WriteTransactionResult>, super::DittoError>
where
for<'batch> F: FnOnce(ScopedStore<'batch>) -> Action<'batch>,
{
use super::*;
let hint: Option<char_p::Ref<'_>> = None;
let mut txn = ffi_sdk::ditto_write_transaction(&*store.ditto, hint).ok()?;
let mut results = vec![];
let batch = ScopedStore {
txn: &mut *txn,
store, // The &Store contains an Arc to the root Ditto instance
results: &mut results,
_txn_id: TypeLevelId::default(),
};
match f(batch).0 {
ActionKind::Commit => {
let status = ffi_sdk::ditto_write_transaction_commit(&*store.ditto, txn);
if status != 0 {
return Err(DittoError::from_ffi(ErrorKind::Internal));
}
}
ActionKind::Rollback => {
ffi_sdk::ditto_write_transaction_rollback(&*store.ditto, txn);
}
}
Ok(results)
}
/// Identifier for changes
pub struct Action<'batch>(ActionKind, TypeLevelId<'batch>);
enum ActionKind {
Commit,
Rollback,
}
impl<'batch> ScopedStore<'batch> {
/// Commit changes done in this ScopedStore.
pub fn commit_changes(self: ScopedStore<'batch>) -> Action<'batch> {
Action(ActionKind::Commit, TypeLevelId::default())
}
/// Revert changes done in this ScopedStore.
pub fn revert_changes(self: ScopedStore<'batch>) -> Action<'batch> {
Action(ActionKind::Rollback, TypeLevelId::default())
}
/// Return an handle to change a single [`Collection`].
pub fn collection<'coll>(
self: &'coll mut ScopedStore<'batch>,
collection_name: &'_ str,
) -> ScopedCollection<'coll, 'batch> {
let c_name = char_p::new(collection_name);
ScopedCollection {
batch: self,
collection_name: c_name,
}
}
}
/// Provides information about the result of an operation on a document
/// that was part of a write transaction.
pub struct WriteTransactionResult {
/// The name of the collection that the operation took place in.
pub collection_name: char_p::Box,
/// The Id of the document that the write transaction result applies to.
pub doc_id: DocumentId,
/// The type of write transaction result.
pub kind: DocChangeKind,
}
#[derive(Debug, PartialEq, Eq)]
/// The types of write transaction result.
pub enum DocChangeKind {
Inserted,
Removed,
Evicted,
Updated,
}