Compare commits
7 commits
master
...
fluffyemil
Author | SHA1 | Date | |
---|---|---|---|
|
bb250a5798 | ||
|
133cad35b6 | ||
|
312d3bf4e0 | ||
|
dddcd1c17f | ||
|
ffff8df490 | ||
|
3865803981 | ||
|
d8bde6ed97 |
25 changed files with 4226 additions and 116 deletions
802
ffi/src/lib.rs
802
ffi/src/lib.rs
|
@ -87,11 +87,12 @@ use std::vec;
|
||||||
|
|
||||||
pub use mentat::{
|
pub use mentat::{
|
||||||
Binding,
|
Binding,
|
||||||
|
CacheDirection,
|
||||||
Entid,
|
Entid,
|
||||||
FindSpec,
|
FindSpec,
|
||||||
HasSchema,
|
HasSchema,
|
||||||
|
InProgress,
|
||||||
KnownEntid,
|
KnownEntid,
|
||||||
Keyword,
|
|
||||||
Queryable,
|
Queryable,
|
||||||
QueryBuilder,
|
QueryBuilder,
|
||||||
QueryInputs,
|
QueryInputs,
|
||||||
|
@ -126,6 +127,7 @@ pub use utils::strings::{
|
||||||
|
|
||||||
pub use utils::log;
|
pub use utils::log;
|
||||||
|
|
||||||
|
// type aliases for iterator types.
|
||||||
pub type BindingIterator = vec::IntoIter<Binding>;
|
pub type BindingIterator = vec::IntoIter<Binding>;
|
||||||
pub type BindingListIterator = std::slice::Chunks<'static, mentat::Binding>;
|
pub type BindingListIterator = std::slice::Chunks<'static, mentat::Binding>;
|
||||||
|
|
||||||
|
@ -221,6 +223,13 @@ impl<T, E> From<Result<T, E>> for ExternResult where E: std::error::Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct InProgressTransactResult<'a, 'c> {
|
||||||
|
pub in_progress: *mut InProgress<'a, 'c>,
|
||||||
|
pub result: *mut ExternResult,
|
||||||
|
}
|
||||||
|
|
||||||
/// A store cannot be opened twice to the same location.
|
/// A store cannot be opened twice to the same location.
|
||||||
/// Once created, the reference to the store is held by the caller and not Rust,
|
/// Once created, the reference to the store is held by the caller and not Rust,
|
||||||
/// therefore the caller is responsible for calling `destroy` to release the memory
|
/// therefore the caller is responsible for calling `destroy` to release the memory
|
||||||
|
@ -247,17 +256,737 @@ pub extern "C" fn store_open(uri: *const c_char) -> *mut Store {
|
||||||
|
|
||||||
// TODO: begin_read
|
// TODO: begin_read
|
||||||
|
|
||||||
// TODO: begin_transaction
|
/// Starts a new transaction to allow multiple transacts to be
|
||||||
|
/// performed together. This is more efficient than performing
|
||||||
|
/// a large set of individual commits.
|
||||||
|
///
|
||||||
|
/// Returns as [Result<TxReport>](mentat::TxReport) as an [ExternResult](ExternResult).
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Callers must ensure that the pointer to the [Store](mentat::Store) is not dangling.
|
||||||
|
///
|
||||||
|
/// Callers are responsible for managing the memory for the return value.
|
||||||
|
/// A destructor `tx_report_destroy` is provided for releasing the memory for this
|
||||||
|
/// pointer type.
|
||||||
|
///
|
||||||
|
/// TODO: Document the errors that can result from begin_transaction
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn store_begin_transaction(store: *mut Store) -> *mut ExternResult {
|
||||||
|
let store = &mut*store;
|
||||||
|
Box::into_raw(Box::new(store.begin_transaction().into()))
|
||||||
|
}
|
||||||
|
|
||||||
/// Performs a single transaction against the store.
|
/// Perform a single transact operation using the current in progress
|
||||||
|
/// transaction. Takes edn as a string to transact.
|
||||||
|
///
|
||||||
|
/// Returns as [Result<TxReport>](mentat::TxReport) as an [ExternResult](ExternResult).
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// Callers are responsible for managing the memory for the return value.
|
/// Callers are responsible for managing the memory for the return value.
|
||||||
/// A destructor `destroy` is provided for releasing the memory for this
|
/// A destructor `tx_report_destroy` is provided for releasing the memory for this
|
||||||
/// pointer type.
|
/// pointer type.
|
||||||
///
|
///
|
||||||
// TODO: Document the errors that can result from transact
|
/// TODO: Document the errors that can result from transact
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn in_progress_transact<'m>(in_progress: *mut InProgress<'m, 'm>, transaction: *const c_char) -> *mut ExternResult {
|
||||||
|
let in_progress = &mut*in_progress;
|
||||||
|
let transaction = c_char_to_string(transaction);
|
||||||
|
Box::into_raw(Box::new(in_progress.transact(transaction).into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Commit all the transacts that have been performed using this
|
||||||
|
/// in progress transaction.
|
||||||
|
///
|
||||||
|
/// Returns as [Result<()>](std::result::Result) as an [ExternResult](ExternResult).
|
||||||
|
///
|
||||||
|
/// TODO: Document the errors that can result from transact
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn in_progress_commit<'m>(in_progress: *mut InProgress<'m, 'm>) -> *mut ExternResult {
|
||||||
|
let in_progress = Box::from_raw(in_progress);
|
||||||
|
Box::into_raw(Box::new(in_progress.commit().into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rolls back all the transacts that have been performed using this
|
||||||
|
/// in progress transaction.
|
||||||
|
///
|
||||||
|
/// Returns as [Result<()>](std::result::Result) as an [ExternResult](ExternResult).
|
||||||
|
///
|
||||||
|
/// TODO: Document the errors that can result from rollback
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn in_progress_rollback<'m>(in_progress: *mut InProgress<'m, 'm>) -> *mut ExternResult {
|
||||||
|
let in_progress = Box::from_raw(in_progress);
|
||||||
|
Box::into_raw(Box::new(in_progress.rollback().into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a builder using the in progress transaction to allow for programmatic
|
||||||
|
/// assertion of values.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Callers are responsible for managing the memory for the return value.
|
||||||
|
/// A destructor `in_progress_builder_destroy` is provided for releasing the memory for this
|
||||||
|
/// pointer type.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn in_progress_builder<'m>(in_progress: *mut InProgress<'m, 'm>) -> *mut InProgressBuilder {
|
||||||
|
let in_progress = Box::from_raw(in_progress);
|
||||||
|
Box::into_raw(Box::new(in_progress.builder()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a builder for an entity with `tempid` using the in progress transaction to
|
||||||
|
/// allow for programmatic assertion of values for that entity.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Callers are responsible for managing the memory for the return value.
|
||||||
|
/// A destructor `entity_builder_destroy` is provided for releasing the memory for this
|
||||||
|
/// pointer type.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn in_progress_entity_builder_from_temp_id<'m>(in_progress: *mut InProgress<'m, 'm>, temp_id: *const c_char) -> *mut EntityBuilder<InProgressBuilder> {
|
||||||
|
let in_progress = Box::from_raw(in_progress);
|
||||||
|
let temp_id = c_char_to_string(temp_id);
|
||||||
|
Box::into_raw(Box::new(in_progress.builder().describe_tempid(&temp_id)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a builder for an entity with `entid` using the in progress transaction to
|
||||||
|
/// allow for programmatic assertion of values for that entity.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Callers are responsible for managing the memory for the return value.
|
||||||
|
/// A destructor `entity_builder_destroy` is provided for releasing the memory for this
|
||||||
|
/// pointer type.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn in_progress_entity_builder_from_entid<'m>(in_progress: *mut InProgress<'m, 'm>, entid: c_longlong) -> *mut EntityBuilder<InProgressBuilder> {
|
||||||
|
let in_progress = Box::from_raw(in_progress);
|
||||||
|
Box::into_raw(Box::new(in_progress.builder().describe(&KnownEntid(entid))))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Starts a new transaction and creates a builder using the that transaction
|
||||||
|
/// to allow for programmatic assertion of values.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Callers are responsible for managing the memory for the return value.
|
||||||
|
/// A destructor `in_progress_builder_destroy` is provided for releasing the memory for this
|
||||||
|
/// pointer type.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn store_in_progress_builder(store: *mut Store) -> *mut ExternResult {
|
||||||
|
let store = &mut*store;
|
||||||
|
let result = store.begin_transaction().and_then(|in_progress| {
|
||||||
|
Ok(in_progress.builder())
|
||||||
|
});
|
||||||
|
Box::into_raw(Box::new(result.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Starts a new transaction and creates a builder for an entity with `tempid`
|
||||||
|
/// using the that transaction to allow for programmatic assertion of values for that entity.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Callers are responsible for managing the memory for the return value.
|
||||||
|
/// A destructor `entity_builder_destroy` is provided for releasing the memory for this
|
||||||
|
/// pointer type.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn store_entity_builder_from_temp_id(store: *mut Store, temp_id: *const c_char) -> *mut ExternResult {
|
||||||
|
let store = &mut*store;
|
||||||
|
let temp_id = c_char_to_string(temp_id);
|
||||||
|
let result = store.begin_transaction().and_then(|in_progress| {
|
||||||
|
Ok(in_progress.builder().describe_tempid(&temp_id))
|
||||||
|
});
|
||||||
|
Box::into_raw(Box::new(result.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Starts a new transaction and creates a builder for an entity with `entid`
|
||||||
|
/// using the that transaction to allow for programmatic assertion of values for that entity.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Callers are responsible for managing the memory for the return value.
|
||||||
|
/// A destructor `entity_builder_destroy` is provided for releasing the memory for this
|
||||||
|
/// pointer type.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn store_entity_builder_from_entid(store: *mut Store, entid: c_longlong) -> *mut ExternResult {
|
||||||
|
let store = &mut*store;
|
||||||
|
let result = store.begin_transaction().and_then(|in_progress| {
|
||||||
|
Ok(in_progress.builder().describe(&KnownEntid(entid)))
|
||||||
|
});
|
||||||
|
Box::into_raw(Box::new(result.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses `builder` to assert `value` for `kw` on entity `entid`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If `entid` is not present in the store.
|
||||||
|
/// If `kw` is not a valid attribute in the store.
|
||||||
|
/// If the type of `kw` is not `:db.type/string`.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn in_progress_builder_add_string<'a, 'c>(builder: *mut InProgressBuilder<'a, 'c>, entid: c_longlong, kw: *const c_char, value: *const c_char) -> *mut ExternResult {
|
||||||
|
let builder = &mut*builder;
|
||||||
|
let kw = kw_from_string(c_char_to_string(kw));
|
||||||
|
let value: TypedValue = c_char_to_string(value).into();
|
||||||
|
Box::into_raw(Box::new(builder.add_kw(KnownEntid(entid), &kw, value).into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses `builder` to assert `value` for `kw` on entity `entid`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If `entid` is not present in the store.
|
||||||
|
/// If `kw` is not a valid attribute in the store.
|
||||||
|
/// If the type of `kw` is not `:db.type/long`.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn in_progress_builder_add_long<'a, 'c>(builder: *mut InProgressBuilder<'a, 'c>, entid: c_longlong, kw: *const c_char, value: c_longlong) -> *mut ExternResult {
|
||||||
|
let builder = &mut*builder;
|
||||||
|
let kw = kw_from_string(c_char_to_string(kw));
|
||||||
|
let value: TypedValue = TypedValue::Long(value);
|
||||||
|
Box::into_raw(Box::new(builder.add_kw(KnownEntid(entid), &kw, value).into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses `builder` to assert `value` for `kw` on entity `entid`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If `entid` is not present in the store.
|
||||||
|
/// If `kw` is not a valid attribute in the store.
|
||||||
|
/// If `value` is not present as an Entid in the store.
|
||||||
|
/// If the type of `kw` is not `:db.type/ref`.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn in_progress_builder_add_ref<'a, 'c>(builder: *mut InProgressBuilder<'a, 'c>, entid: c_longlong, kw: *const c_char, value: c_longlong) -> *mut ExternResult {
|
||||||
|
let builder = &mut*builder;
|
||||||
|
let kw = kw_from_string(c_char_to_string(kw));
|
||||||
|
let value: TypedValue = TypedValue::Ref(value);
|
||||||
|
Box::into_raw(Box::new(builder.add_kw(KnownEntid(entid), &kw, value).into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses `builder` to assert `value` for `kw` on entity `entid`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If `entid` is not present in the store.
|
||||||
|
/// If `kw` is not a valid attribute in the store.
|
||||||
|
/// If `value` is not present as an attribute in the store.
|
||||||
|
/// If the type of `kw` is not `:db.type/keyword`.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn in_progress_builder_add_keyword<'a, 'c>(builder: *mut InProgressBuilder<'a, 'c>, entid: c_longlong, kw: *const c_char, value: *const c_char) -> *mut ExternResult {
|
||||||
|
let builder = &mut*builder;
|
||||||
|
let kw = kw_from_string(c_char_to_string(kw));
|
||||||
|
let value: TypedValue = kw_from_string(c_char_to_string(value)).into();
|
||||||
|
Box::into_raw(Box::new(builder.add_kw(KnownEntid(entid), &kw, value).into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses `builder` to assert `value` for `kw` on entity `entid`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If `entid` is not present in the store.
|
||||||
|
/// If `kw` is not a valid attribute in the store.
|
||||||
|
/// If the type of `kw` is not `:db.type/boolean`.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn in_progress_builder_add_boolean<'a, 'c>(builder: *mut InProgressBuilder<'a, 'c>, entid: c_longlong, kw: *const c_char, value: bool) -> *mut ExternResult {
|
||||||
|
let builder = &mut*builder;
|
||||||
|
let kw = kw_from_string(c_char_to_string(kw));
|
||||||
|
let value: TypedValue = value.into();
|
||||||
|
Box::into_raw(Box::new(builder.add_kw(KnownEntid(entid), &kw, value).into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses `builder` to assert `value` for `kw` on entity `entid`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If `entid` is not present in the store.
|
||||||
|
/// If `kw` is not a valid attribute in the store.
|
||||||
|
/// If the type of `kw` is not `:db.type/double`.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn in_progress_builder_add_double<'a, 'c>(builder: *mut InProgressBuilder<'a, 'c>, entid: c_longlong, kw: *const c_char, value: f64) -> *mut ExternResult {
|
||||||
|
let builder = &mut*builder;
|
||||||
|
let kw = kw_from_string(c_char_to_string(kw));
|
||||||
|
let value: TypedValue = value.into();
|
||||||
|
Box::into_raw(Box::new(builder.add_kw(KnownEntid(entid), &kw, value).into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses `builder` to assert `value` for `kw` on entity `entid`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If `entid` is not present in the store.
|
||||||
|
/// If `kw` is not a valid attribute in the store.
|
||||||
|
/// If the type of `kw` is not `:db.type/instant`.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn in_progress_builder_add_timestamp<'a, 'c>(builder: *mut InProgressBuilder<'a, 'c>, entid: c_longlong, kw: *const c_char, value: c_longlong) -> *mut ExternResult {
|
||||||
|
let builder = &mut*builder;
|
||||||
|
let kw = kw_from_string(c_char_to_string(kw));
|
||||||
|
let value: TypedValue = TypedValue::instant(value);
|
||||||
|
Box::into_raw(Box::new(builder.add_kw(KnownEntid(entid), &kw, value).into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses `builder` to assert `value` for `kw` on entity `entid`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If `entid` is not present in the store.
|
||||||
|
/// If `kw` is not a valid attribute in the store.
|
||||||
|
/// If the type of `kw` is not `:db.type/uuid`.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn in_progress_builder_add_uuid<'a, 'c>(builder: *mut InProgressBuilder<'a, 'c>, entid: c_longlong, kw: *const c_char, value: *mut [u8; 16]) -> *mut ExternResult {
|
||||||
|
let builder = &mut*builder;
|
||||||
|
let kw = kw_from_string(c_char_to_string(kw));
|
||||||
|
let value = &*value;
|
||||||
|
let value = Uuid::from_bytes(value).expect("valid uuid");
|
||||||
|
let value: TypedValue = value.into();
|
||||||
|
Box::into_raw(Box::new(builder.add_kw(KnownEntid(entid), &kw, value).into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses `builder` to retract `value` for `kw` on entity `entid`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If `entid` is not present in the store.
|
||||||
|
/// If `kw` is not a valid attribute in the store.
|
||||||
|
/// If the type of `kw` is not `:db.type/string`.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn in_progress_builder_retract_string<'a, 'c>(builder: *mut InProgressBuilder<'a, 'c>, entid: c_longlong, kw: *const c_char, value: *const c_char) -> *mut ExternResult {
|
||||||
|
let builder = &mut*builder;
|
||||||
|
let kw = kw_from_string(c_char_to_string(kw));
|
||||||
|
let value: TypedValue = c_char_to_string(value).into();
|
||||||
|
Box::into_raw(Box::new(builder.retract_kw(KnownEntid(entid), &kw, value).into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses `builder` to retract `value` for `kw` on entity `entid`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If `entid` is not present in the store.
|
||||||
|
/// If `kw` is not a valid attribute in the store.
|
||||||
|
/// If the type of `kw` is not `:db.type/long`.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn in_progress_builder_retract_long<'a, 'c>(builder: *mut InProgressBuilder<'a, 'c>, entid: c_longlong, kw: *const c_char, value: c_longlong) -> *mut ExternResult {
|
||||||
|
let builder = &mut*builder;
|
||||||
|
let kw = kw_from_string(c_char_to_string(kw));
|
||||||
|
let value: TypedValue = TypedValue::Long(value);
|
||||||
|
Box::into_raw(Box::new(builder.retract_kw(KnownEntid(entid), &kw, value).into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses `builder` to retract `value` for `kw` on entity `entid`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If `entid` is not present in the store.
|
||||||
|
/// If `kw` is not a valid attribute in the store.
|
||||||
|
/// If the type of `kw` is not `:db.type/ref`.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn in_progress_builder_retract_ref<'a, 'c>(builder: *mut InProgressBuilder<'a, 'c>, entid: c_longlong, kw: *const c_char, value: c_longlong) -> *mut ExternResult {
|
||||||
|
let builder = &mut*builder;
|
||||||
|
let kw = kw_from_string(c_char_to_string(kw));
|
||||||
|
let value: TypedValue = TypedValue::Ref(value);
|
||||||
|
Box::into_raw(Box::new(builder.retract_kw(KnownEntid(entid), &kw, value).into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Uses `builder` to retract `value` for `kw` on entity `entid`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If `entid` is not present in the store.
|
||||||
|
/// If `kw` is not a valid attribute in the store.
|
||||||
|
/// If the type of `kw` is not `:db.type/keyword`.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn in_progress_builder_retract_keyword<'a, 'c>(builder: *mut InProgressBuilder<'a, 'c>, entid: c_longlong, kw: *const c_char, value: *const c_char) -> *mut ExternResult {
|
||||||
|
let builder = &mut*builder;
|
||||||
|
let kw = kw_from_string(c_char_to_string(kw));
|
||||||
|
let value: TypedValue = kw_from_string(c_char_to_string(value)).into();
|
||||||
|
Box::into_raw(Box::new(builder.retract_kw(KnownEntid(entid), &kw, value).into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses `builder` to retract `value` for `kw` on entity `entid`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If `entid` is not present in the store.
|
||||||
|
/// If `kw` is not a valid attribute in the store.
|
||||||
|
/// If the type of `kw` is not `:db.type/boolean`.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn in_progress_builder_retract_boolean<'a, 'c>(builder: *mut InProgressBuilder<'a, 'c>, entid: c_longlong, kw: *const c_char, value: bool) -> *mut ExternResult {
|
||||||
|
let builder = &mut*builder;
|
||||||
|
let kw = kw_from_string(c_char_to_string(kw));
|
||||||
|
let value: TypedValue = value.into();
|
||||||
|
Box::into_raw(Box::new(builder.retract_kw(KnownEntid(entid), &kw, value).into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses `builder` to retract `value` for `kw` on entity `entid`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If `entid` is not present in the store.
|
||||||
|
/// If `kw` is not a valid attribute in the store.
|
||||||
|
/// If the type of `kw` is not `:db.type/double`.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn in_progress_builder_retract_double<'a, 'c>(builder: *mut InProgressBuilder<'a, 'c>, entid: c_longlong, kw: *const c_char, value: f64) -> *mut ExternResult {
|
||||||
|
let builder = &mut*builder;
|
||||||
|
let kw = kw_from_string(c_char_to_string(kw));
|
||||||
|
let value: TypedValue = value.into();
|
||||||
|
Box::into_raw(Box::new(builder.retract_kw(KnownEntid(entid), &kw, value).into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses `builder` to retract `value` for `kw` on entity `entid`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If `entid` is not present in the store.
|
||||||
|
/// If `kw` is not a valid attribute in the store.
|
||||||
|
/// If the type of `kw` is not `:db.type/instant`.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn in_progress_builder_retract_timestamp<'a, 'c>(builder: *mut InProgressBuilder<'a, 'c>, entid: c_longlong, kw: *const c_char, value: c_longlong) -> *mut ExternResult {
|
||||||
|
let builder = &mut*builder;
|
||||||
|
let kw = kw_from_string(c_char_to_string(kw));
|
||||||
|
let value: TypedValue = TypedValue::instant(value);
|
||||||
|
Box::into_raw(Box::new(builder.retract_kw(KnownEntid(entid), &kw, value).into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses `builder` to retract `value` for `kw` on entity `entid`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If `entid` is not present in the store.
|
||||||
|
/// If `kw` is not a valid attribute in the store.
|
||||||
|
/// If the type of `kw` is not `:db.type/uuid`.
|
||||||
|
///
|
||||||
|
/// TODO don't panic if the UUID is not valid - return result instead.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn in_progress_builder_retract_uuid<'a, 'c>(builder: *mut InProgressBuilder<'a, 'c>, entid: c_longlong, kw: *const c_char, value: *mut [u8; 16]) -> *mut ExternResult {
|
||||||
|
let builder = &mut*builder;
|
||||||
|
let kw = kw_from_string(c_char_to_string(kw));
|
||||||
|
let value = &*value;
|
||||||
|
let value = Uuid::from_bytes(value).expect("valid uuid");
|
||||||
|
let value: TypedValue = value.into();
|
||||||
|
Box::into_raw(Box::new(builder.retract_kw(KnownEntid(entid), &kw, value).into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transacts and commits all the assertions and retractions that have been performed
|
||||||
|
/// using this builder.
|
||||||
|
///
|
||||||
|
/// This consumes the builder and the enclosed [InProgress](mentat::InProgress) transaction.
|
||||||
|
///
|
||||||
|
/// Returns as [Result<()>(std::result::Result) as an [ExternResult](ExternResult).
|
||||||
|
///
|
||||||
|
/// TODO: Document the errors that can result from transact
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn in_progress_builder_commit<'a, 'c>(builder: *mut InProgressBuilder<'a, 'c>) -> *mut ExternResult {
|
||||||
|
let builder = Box::from_raw(builder);
|
||||||
|
Box::into_raw(Box::new(builder.commit().into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transacts all the assertions and retractions that have been performed
|
||||||
|
/// using this builder.
|
||||||
|
///
|
||||||
|
/// This consumes the builder and returns the enclosed [InProgress](mentat::InProgress) transaction
|
||||||
|
/// inside the [InProgressTransactResult](mentat::InProgressTransactResult) alongside the [TxReport](mentat::TxReport) generated
|
||||||
|
/// by the transact.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Callers are responsible for managing the memory for the return value.
|
||||||
|
/// The destructors `in_progress_destroy` and `tx_report_destroy` arew provided for
|
||||||
|
/// releasing the memory for these pointer types.
|
||||||
|
///
|
||||||
|
/// TODO: Document the errors that can result from transact
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn in_progress_builder_transact<'a, 'c>(builder: *mut InProgressBuilder<'a, 'c>) -> *mut InProgressTransactResult<'a, 'c> {
|
||||||
|
let builder = Box::from_raw(builder);
|
||||||
|
let (in_progress, tx_report) = builder.transact();
|
||||||
|
let result = InProgressTransactResult { in_progress: Box::into_raw(Box::new(in_progress)), result: Box::into_raw(Box::new(tx_report.into())) };
|
||||||
|
Box::into_raw(Box::new(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses `builder` to assert `value` for `kw` on entity `entid`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If `entid` is not present in the store.
|
||||||
|
/// If `kw` is not a valid attribute in the store.
|
||||||
|
/// If the type of `kw` is not `:db.type/string`.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn entity_builder_add_string<'a, 'c>(builder: *mut EntityBuilder<InProgressBuilder<'a, 'c>>, kw: *const c_char, value: *const c_char) -> *mut ExternResult {
|
||||||
|
let builder = &mut*builder;
|
||||||
|
let kw = kw_from_string(c_char_to_string(kw));
|
||||||
|
let value: TypedValue = c_char_to_string(value).into();
|
||||||
|
Box::into_raw(Box::new(builder.add_kw(&kw, value).into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses `builder` to assert `value` for `kw` on entity `entid`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If `entid` is not present in the store.
|
||||||
|
/// If `kw` is not a valid attribute in the store.
|
||||||
|
/// If the type of `kw` is not `:db.type/long`.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn entity_builder_add_long<'a, 'c>(builder: *mut EntityBuilder<InProgressBuilder<'a, 'c>>, kw: *const c_char, value: c_longlong) -> *mut ExternResult {
|
||||||
|
let builder = &mut*builder;
|
||||||
|
let kw = kw_from_string(c_char_to_string(kw));
|
||||||
|
let value: TypedValue = TypedValue::Long(value);
|
||||||
|
Box::into_raw(Box::new(builder.add_kw(&kw, value).into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses `builder` to assert `value` for `kw` on entity `entid`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If `entid` is not present in the store.
|
||||||
|
/// If `kw` is not a valid attribute in the store.
|
||||||
|
/// If the type of `kw` is not `:db.type/ref`.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn entity_builder_add_ref<'a, 'c>(builder: *mut EntityBuilder<InProgressBuilder<'a, 'c>>, kw: *const c_char, value: c_longlong) -> *mut ExternResult {
|
||||||
|
let builder = &mut*builder;
|
||||||
|
let kw = kw_from_string(c_char_to_string(kw));
|
||||||
|
let value: TypedValue = TypedValue::Ref(value);
|
||||||
|
Box::into_raw(Box::new(builder.add_kw(&kw, value).into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses `builder` to assert `value` for `kw` on entity `entid`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If `entid` is not present in the store.
|
||||||
|
/// If `kw` is not a valid attribute in the store.
|
||||||
|
/// If the type of `kw` is not `:db.type/keyword`.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn entity_builder_add_keyword<'a, 'c>(builder: *mut EntityBuilder<InProgressBuilder<'a, 'c>>, kw: *const c_char, value: *const c_char) -> *mut ExternResult {
|
||||||
|
let builder = &mut*builder;
|
||||||
|
let kw = kw_from_string(c_char_to_string(kw));
|
||||||
|
let value: TypedValue = kw_from_string(c_char_to_string(value)).into();
|
||||||
|
Box::into_raw(Box::new(builder.add_kw(&kw, value).into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses `builder` to assert `value` for `kw` on entity `entid`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If `entid` is not present in the store.
|
||||||
|
/// If `kw` is not a valid attribute in the store.
|
||||||
|
/// If the type of `kw` is not `:db.type/boolean`.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn entity_builder_add_boolean<'a, 'c>(builder: *mut EntityBuilder<InProgressBuilder<'a, 'c>>, kw: *const c_char, value: bool) -> *mut ExternResult {
|
||||||
|
let builder = &mut*builder;
|
||||||
|
let kw = kw_from_string(c_char_to_string(kw));
|
||||||
|
let value: TypedValue = value.into();
|
||||||
|
Box::into_raw(Box::new(builder.add_kw(&kw, value).into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses `builder` to assert `value` for `kw` on entity `entid`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If `entid` is not present in the store.
|
||||||
|
/// If `kw` is not a valid attribute in the store.
|
||||||
|
/// If the type of `kw` is not `:db.type/double`.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn entity_builder_add_double<'a, 'c>(builder: *mut EntityBuilder<InProgressBuilder<'a, 'c>>, kw: *const c_char, value: f64) -> *mut ExternResult {
|
||||||
|
let builder = &mut*builder;
|
||||||
|
let kw = kw_from_string(c_char_to_string(kw));
|
||||||
|
let value: TypedValue = value.into();
|
||||||
|
Box::into_raw(Box::new(builder.add_kw(&kw, value).into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses `builder` to assert `value` for `kw` on entity `entid`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If `entid` is not present in the store.
|
||||||
|
/// If `kw` is not a valid attribute in the store.
|
||||||
|
/// If the type of `kw` is not `:db.type/instant`.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn entity_builder_add_timestamp<'a, 'c>(builder: *mut EntityBuilder<InProgressBuilder<'a, 'c>>, kw: *const c_char, value: c_longlong) -> *mut ExternResult {
|
||||||
|
let builder = &mut*builder;
|
||||||
|
let kw = kw_from_string(c_char_to_string(kw));
|
||||||
|
let value: TypedValue = TypedValue::instant(value);
|
||||||
|
Box::into_raw(Box::new(builder.add_kw(&kw, value).into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses `builder` to assert `value` for `kw` on entity `entid`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If `entid` is not present in the store.
|
||||||
|
/// If `kw` is not a valid attribute in the store.
|
||||||
|
/// If the type of `kw` is not `:db.type/uuid`.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn entity_builder_add_uuid<'a, 'c>(builder: *mut EntityBuilder<InProgressBuilder<'a, 'c>>, kw: *const c_char, value: *mut [u8; 16]) -> *mut ExternResult {
|
||||||
|
let builder = &mut*builder;
|
||||||
|
let kw = kw_from_string(c_char_to_string(kw));
|
||||||
|
let value = &*value;
|
||||||
|
let value = Uuid::from_bytes(value).expect("valid uuid");
|
||||||
|
let value: TypedValue = value.into();
|
||||||
|
Box::into_raw(Box::new(builder.add_kw(&kw, value).into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses `builder` to retract `value` for `kw` on entity `entid`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If `entid` is not present in the store.
|
||||||
|
/// If `kw` is not a valid attribute in the store.
|
||||||
|
/// If the type of `kw` is not `:db.type/string`.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn entity_builder_retract_string<'a, 'c>(builder: *mut EntityBuilder<InProgressBuilder<'a, 'c>>, kw: *const c_char, value: *const c_char) -> *mut ExternResult {
|
||||||
|
let builder = &mut*builder;
|
||||||
|
let kw = kw_from_string(c_char_to_string(kw));
|
||||||
|
let value: TypedValue = c_char_to_string(value).into();
|
||||||
|
Box::into_raw(Box::new(builder.retract_kw(&kw, value).into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses `builder` to retract `value` for `kw` on entity `entid`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If `entid` is not present in the store.
|
||||||
|
/// If `kw` is not a valid attribute in the store.
|
||||||
|
/// If the type of `kw` is not `:db.type/long`.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn entity_builder_retract_long<'a, 'c>(builder: *mut EntityBuilder<InProgressBuilder<'a, 'c>>, kw: *const c_char, value: c_longlong) -> *mut ExternResult {
|
||||||
|
let builder = &mut*builder;
|
||||||
|
let kw = kw_from_string(c_char_to_string(kw));
|
||||||
|
let value: TypedValue = TypedValue::Long(value);
|
||||||
|
Box::into_raw(Box::new(builder.retract_kw(&kw, value).into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses `builder` to retract `value` for `kw` on entity `entid`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If `entid` is not present in the store.
|
||||||
|
/// If `kw` is not a valid attribute in the store.
|
||||||
|
/// If the type of `kw` is not `:db.type/ref`.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn entity_builder_retract_ref<'a, 'c>(builder: *mut EntityBuilder<InProgressBuilder<'a, 'c>>, kw: *const c_char, value: c_longlong) -> *mut ExternResult {
|
||||||
|
let builder = &mut*builder;
|
||||||
|
let kw = kw_from_string(c_char_to_string(kw));
|
||||||
|
let value: TypedValue = TypedValue::Ref(value);
|
||||||
|
Box::into_raw(Box::new(builder.retract_kw(&kw, value).into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses `builder` to retract `value` for `kw` on entity `entid`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If `entid` is not present in the store.
|
||||||
|
/// If `kw` is not a valid attribute in the store.
|
||||||
|
/// If the type of `kw` is not `:db.type/keyword`.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn entity_builder_retract_keyword<'a, 'c>(builder: *mut EntityBuilder<InProgressBuilder<'a, 'c>>, kw: *const c_char, value: *const c_char) -> *mut ExternResult {
|
||||||
|
let builder = &mut*builder;
|
||||||
|
let kw = kw_from_string(c_char_to_string(kw));
|
||||||
|
let value: TypedValue = kw_from_string(c_char_to_string(value)).into();
|
||||||
|
Box::into_raw(Box::new(builder.retract_kw(&kw, value).into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Uses `builder` to retract `value` for `kw` on entity `entid`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If `entid` is not present in the store.
|
||||||
|
/// If `kw` is not a valid attribute in the store.
|
||||||
|
/// If the type of `kw` is not `:db.type/boolean`.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn entity_builder_retract_boolean<'a, 'c>(builder: *mut EntityBuilder<InProgressBuilder<'a, 'c>>, kw: *const c_char, value: bool) -> *mut ExternResult {
|
||||||
|
let builder = &mut*builder;
|
||||||
|
let kw = kw_from_string(c_char_to_string(kw));
|
||||||
|
let value: TypedValue = value.into();
|
||||||
|
Box::into_raw(Box::new(builder.retract_kw(&kw, value).into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses `builder` to retract `value` for `kw` on entity `entid`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If `entid` is not present in the store.
|
||||||
|
/// If `kw` is not a valid attribute in the store.
|
||||||
|
/// If the type of `kw` is not `:db.type/double`.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn entity_builder_retract_double<'a, 'c>(builder: *mut EntityBuilder<InProgressBuilder<'a, 'c>>, kw: *const c_char, value: f64) -> *mut ExternResult {
|
||||||
|
let builder = &mut*builder;
|
||||||
|
let kw = kw_from_string(c_char_to_string(kw));
|
||||||
|
let value: TypedValue = value.into();
|
||||||
|
Box::into_raw(Box::new(builder.retract_kw(&kw, value).into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses `builder` to retract `value` for `kw` on entity `entid`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If `entid` is not present in the store.
|
||||||
|
/// If `kw` is not a valid attribute in the store.
|
||||||
|
/// If the type of `kw` is not `:db.type/instant`.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn entity_builder_retract_timestamp<'a, 'c>(builder: *mut EntityBuilder<InProgressBuilder<'a, 'c>>, kw: *const c_char, value: c_longlong) -> *mut ExternResult {
|
||||||
|
let builder = &mut*builder;
|
||||||
|
let kw = kw_from_string(c_char_to_string(kw));
|
||||||
|
let value: TypedValue = TypedValue::instant(value);
|
||||||
|
Box::into_raw(Box::new(builder.retract_kw(&kw, value).into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses `builder` to retract `value` for `kw` on entity `entid`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If `entid` is not present in the store.
|
||||||
|
/// If `kw` is not a valid attribute in the store.
|
||||||
|
/// If the type of `kw` is not `:db.type/uuid`.
|
||||||
|
///
|
||||||
|
/// TODO don't panic if the UUID is not valid - return result instead.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn entity_builder_retract_uuid<'a, 'c>(builder: *mut EntityBuilder<InProgressBuilder<'a, 'c>>, kw: *const c_char, value: *mut [u8; 16]) -> *mut ExternResult {
|
||||||
|
let builder = &mut*builder;
|
||||||
|
let kw = kw_from_string(c_char_to_string(kw));
|
||||||
|
let value = &*value;
|
||||||
|
let value = Uuid::from_bytes(value).expect("valid uuid");
|
||||||
|
let value: TypedValue = value.into();
|
||||||
|
Box::into_raw(Box::new(builder.retract_kw(&kw, value).into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transacts all the assertions and retractions that have been performed
|
||||||
|
/// using this builder.
|
||||||
|
///
|
||||||
|
/// This consumes the builder and returns the enclosed [InProgress](mentat::InProgress) transaction
|
||||||
|
/// inside the [InProgressTransactResult][::InProgressTransactResult] alongside the [TxReport](mentat::TxReport) generated
|
||||||
|
/// by the transact.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Callers are responsible for managing the memory for the return value.
|
||||||
|
/// The destructors `in_progress_destroy` and `tx_report_destroy` are provided for
|
||||||
|
/// releasing the memory for these pointer types.
|
||||||
|
///
|
||||||
|
/// TODO: Document the errors that can result from transact
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn entity_builder_transact<'a, 'c>(builder: *mut EntityBuilder<InProgressBuilder<'a, 'c>>) -> *mut InProgressTransactResult<'a, 'c> {
|
||||||
|
let builder = Box::from_raw(builder);
|
||||||
|
let (in_progress, tx_report) = builder.transact();
|
||||||
|
let result = InProgressTransactResult { in_progress: Box::into_raw(Box::new(in_progress)), result: Box::into_raw(Box::new(tx_report.into())) };
|
||||||
|
Box::into_raw(Box::new(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transacts and commits all the assertions and retractions that have been performed
|
||||||
|
/// using this builder.
|
||||||
|
///
|
||||||
|
/// This consumes the builder and the enclosed [InProgress](mentat::InProgress) transaction.
|
||||||
|
///
|
||||||
|
/// Returns as [Result](std::result::Result) as an [ExternResult](::ExternResult).
|
||||||
|
///
|
||||||
|
/// TODO: Document the errors that can result from transact
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn entity_builder_commit<'a, 'c>(builder: *mut EntityBuilder<InProgressBuilder<'a, 'c>>) -> *mut ExternResult {
|
||||||
|
let builder = Box::from_raw(builder);
|
||||||
|
Box::into_raw(Box::new(builder.commit().into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs a single transaction against the store.
|
||||||
|
///
|
||||||
|
/// Returns as [TxReport](mentat::TxReport) as an [ExternResult](::ExternResult).
|
||||||
|
/// TODO: Document the errors that can result from transact
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn store_transact(store: *mut Store, transaction: *const c_char) -> *mut ExternResult {
|
pub unsafe extern "C" fn store_transact(store: *mut Store, transaction: *const c_char) -> *mut ExternResult {
|
||||||
let store = &mut*store;
|
let store = &mut*store;
|
||||||
|
@ -271,7 +1000,7 @@ pub unsafe extern "C" fn store_transact(store: *mut Store, transaction: *const c
|
||||||
Box::into_raw(Box::new(result.into()))
|
Box::into_raw(Box::new(result.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetches the `tx_id` for the given [TxReport](mentat::TxReport).
|
/// Fetches the `tx_id` for the given [TxReport](mentat::TxReport)`.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn tx_report_get_entid(tx_report: *mut TxReport) -> c_longlong {
|
pub unsafe extern "C" fn tx_report_get_entid(tx_report: *mut TxReport) -> c_longlong {
|
||||||
let tx_report = &*tx_report;
|
let tx_report = &*tx_report;
|
||||||
|
@ -298,9 +1027,40 @@ pub unsafe extern "C" fn tx_report_entity_for_temp_id(tx_report: *mut TxReport,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: cache
|
/// Adds an attribute to the cache.
|
||||||
|
/// `store_cache_attribute_forward` caches values for an attribute keyed by entity
|
||||||
|
/// (i.e. find values and entities that have this attribute, or find values of attribute for an entity)
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn store_cache_attribute_forward(store: *mut Store, attribute: *const c_char) -> *mut ExternResult {
|
||||||
|
let store = unsafe { &mut *store };
|
||||||
|
let kw = kw_from_string(c_char_to_string(attribute));
|
||||||
|
Box::into_raw(Box::new(store.cache(&kw, CacheDirection::Forward).into()))
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: q_once
|
/// Adds an attribute to the cache.
|
||||||
|
/// `store_cache_attribute_reverse` caches entities for an attribute keyed by value.
|
||||||
|
/// (i.e. find entities that have a particular value for an attribute).
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn store_cache_attribute_reverse(store: *mut Store, attribute: *const c_char) -> *mut ExternResult {
|
||||||
|
let store = unsafe { &mut *store };
|
||||||
|
let kw = kw_from_string(c_char_to_string(attribute));
|
||||||
|
Box::into_raw(Box::new(store.cache(&kw, CacheDirection::Reverse).into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds an attribute to the cache.
|
||||||
|
/// `store_cache_attribute_bi_directional` caches entity in both available directions, forward and reverse.
|
||||||
|
///
|
||||||
|
/// `Forward` caches values for an attribute keyed by entity
|
||||||
|
/// (i.e. find values and entities that have this attribute, or find values of attribute for an entity)
|
||||||
|
///
|
||||||
|
/// `Reverse` caches entities for an attribute keyed by value.
|
||||||
|
/// (i.e. find entities that have a particular value for an attribute).
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn store_cache_attribute_bi_directional(store: *mut Store, attribute: *const c_char) -> *mut ExternResult {
|
||||||
|
let store = unsafe { &mut *store };
|
||||||
|
let kw = kw_from_string(c_char_to_string(attribute));
|
||||||
|
Box::into_raw(Box::new(store.cache(&kw, CacheDirection::Both).into()))
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a [QueryBuilder](mentat::QueryBuilder) from the given store to execute the provided query.
|
/// Creates a [QueryBuilder](mentat::QueryBuilder) from the given store to execute the provided query.
|
||||||
///
|
///
|
||||||
|
@ -510,10 +1270,11 @@ pub unsafe extern "C" fn typed_value_into_long(typed_value: *mut Binding) -> c_l
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// If the [ValueType](mentat::ValueType) of the [Binding](mentat::Binding) is not [ValueType::Long](mentat::ValueType::Ref).
|
/// If the [ValueType](mentat::ValueType) of the [Binding](mentat::Binding) is not [ValueType::Ref](mentat::ValueType::Ref).
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn typed_value_into_entid(typed_value: *mut Binding) -> Entid {
|
pub unsafe extern "C" fn typed_value_into_entid(typed_value: *mut Binding) -> Entid {
|
||||||
let typed_value = Box::from_raw(typed_value);
|
let typed_value = Box::from_raw(typed_value);
|
||||||
|
println!("typed value as entid {:?}", typed_value);
|
||||||
unwrap_conversion(typed_value.into_entid(), ValueType::Ref)
|
unwrap_conversion(typed_value.into_entid(), ValueType::Ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -521,7 +1282,7 @@ pub unsafe extern "C" fn typed_value_into_entid(typed_value: *mut Binding) -> En
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// If the [ValueType](mentat::ValueType) of the [Binding](mentat::Binding) is not [ValueType::Long](mentat::ValueType::Ref).
|
/// If the [ValueType](mentat::ValueType) of the [Binding](mentat::Binding) is not [ValueType::Ref](mentat::ValueType::Ref).
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn typed_value_into_kw(typed_value: *mut Binding) -> *const c_char {
|
pub unsafe extern "C" fn typed_value_into_kw(typed_value: *mut Binding) -> *const c_char {
|
||||||
let typed_value = Box::from_raw(typed_value);
|
let typed_value = Box::from_raw(typed_value);
|
||||||
|
@ -607,7 +1368,7 @@ pub unsafe extern "C" fn row_at_index(rows: *mut RelResult<Binding>, index: c_in
|
||||||
result.row(index as usize).map_or_else(std::ptr::null_mut, |v| Box::into_raw(Box::new(v.to_vec())))
|
result.row(index as usize).map_or_else(std::ptr::null_mut, |v| Box::into_raw(Box::new(v.to_vec())))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consumes the `Vec<Vec<Binding>>` and returns an iterator over the values.
|
/// Consumes the `RelResult<Binding>` and returns an iterator over the values.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
|
@ -905,6 +1666,18 @@ pub unsafe extern "C" fn changelist_entry_at(tx_report: *mut TransactionChange,
|
||||||
tx_report.changes[index].clone()
|
tx_report.changes[index].clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Destructor for releasing the memory of [InProgressBuilder](mentat::InProgressBuilder).
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn in_progress_builder_destroy<'a, 'c>(obj: *mut InProgressBuilder<'a, 'c>) {
|
||||||
|
let _ = Box::from_raw(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Destructor for releasing the memory of [EntityBuilder](mentat::EntityBuilder).
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn entity_builder_destroy<'a, 'c>(obj: *mut EntityBuilder<InProgressBuilder<'a, 'c>>) {
|
||||||
|
let _ = Box::from_raw(obj);
|
||||||
|
}
|
||||||
|
|
||||||
/// destroy function for releasing the memory for `repr(C)` structs.
|
/// destroy function for releasing the memory for `repr(C)` structs.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn destroy(obj: *mut c_void) {
|
pub unsafe extern "C" fn destroy(obj: *mut c_void) {
|
||||||
|
@ -933,12 +1706,17 @@ define_destructor!(tx_report_destroy, TxReport);
|
||||||
/// Destructor for releasing the memory of [Binding](mentat::Binding).
|
/// Destructor for releasing the memory of [Binding](mentat::Binding).
|
||||||
define_destructor!(typed_value_destroy, Binding);
|
define_destructor!(typed_value_destroy, Binding);
|
||||||
|
|
||||||
|
/// Destructor for releasing the memory of [Vec<Binding>][mentat::Binding].
|
||||||
define_destructor!(typed_value_list_destroy, Vec<Binding>);
|
define_destructor!(typed_value_list_destroy, Vec<Binding>);
|
||||||
|
|
||||||
/// Destructor for releasing the memory of [BindingIterator](BindingIterator) .
|
/// Destructor for releasing the memory of [BindingIterator](BindingIterator) .
|
||||||
define_destructor!(typed_value_list_iter_destroy, BindingIterator);
|
define_destructor!(typed_value_list_iter_destroy, BindingIterator);
|
||||||
|
|
||||||
|
/// Destructor for releasing the memory of [RelResult<Binding>](mentat::RelResult).
|
||||||
define_destructor!(typed_value_result_set_destroy, RelResult<Binding>);
|
define_destructor!(typed_value_result_set_destroy, RelResult<Binding>);
|
||||||
|
|
||||||
/// Destructor for releasing the memory of [BindingListIterator](BindingListIterator) .
|
/// Destructor for releasing the memory of [BindingListIterator](::BindingListIterator).
|
||||||
define_destructor!(typed_value_result_set_iter_destroy, BindingListIterator);
|
define_destructor!(typed_value_result_set_iter_destroy, BindingListIterator);
|
||||||
|
|
||||||
|
/// Destructor for releasing the memory of [InProgress](mentat::InProgress).
|
||||||
|
define_destructor!(in_progress_destroy, InProgress);
|
||||||
|
|
|
@ -14,6 +14,7 @@ import android.content.Context;
|
||||||
import android.content.res.AssetManager;
|
import android.content.res.AssetManager;
|
||||||
import android.support.test.InstrumentationRegistry;
|
import android.support.test.InstrumentationRegistry;
|
||||||
import android.support.test.runner.AndroidJUnit4;
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
@ -22,7 +23,6 @@ import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
@ -40,6 +40,33 @@ import static org.junit.Assert.*;
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class FFIIntegrationTest {
|
public class FFIIntegrationTest {
|
||||||
|
|
||||||
|
class DBSetupResult {
|
||||||
|
TxReport schemaReport;
|
||||||
|
TxReport dataReport;
|
||||||
|
|
||||||
|
public DBSetupResult(TxReport schemaReport, TxReport dataReport) {
|
||||||
|
this.schemaReport = schemaReport;
|
||||||
|
this.dataReport = dataReport;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class QueryTimer {
|
||||||
|
private long startTime = 0;
|
||||||
|
private long endTime = 0;
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
this.startTime = System.nanoTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void end() {
|
||||||
|
this.endTime = System.nanoTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long duration() {
|
||||||
|
return this.endTime - this.startTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Mentat mentat = null;
|
Mentat mentat = null;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -95,7 +122,8 @@ public class FFIIntegrationTest {
|
||||||
return this.mentat;
|
return this.mentat;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TxReport populateWithTypesSchema(Mentat mentat) {
|
public DBSetupResult populateWithTypesSchema(Mentat mentat) {
|
||||||
|
InProgress transaction = mentat.beginTransaction();
|
||||||
String schema = "[\n" +
|
String schema = "[\n" +
|
||||||
" [:db/add \"b\" :db/ident :foo/boolean]\n" +
|
" [:db/add \"b\" :db/ident :foo/boolean]\n" +
|
||||||
" [:db/add \"b\" :db/valueType :db.type/boolean]\n" +
|
" [:db/add \"b\" :db/valueType :db.type/boolean]\n" +
|
||||||
|
@ -122,7 +150,7 @@ public class FFIIntegrationTest {
|
||||||
" [:db/add \"u\" :db/valueType :db.type/uuid]\n" +
|
" [:db/add \"u\" :db/valueType :db.type/uuid]\n" +
|
||||||
" [:db/add \"u\" :db/cardinality :db.cardinality/one]\n" +
|
" [:db/add \"u\" :db/cardinality :db.cardinality/one]\n" +
|
||||||
" ]";
|
" ]";
|
||||||
TxReport report = mentat.transact(schema);
|
TxReport report = transaction.transact(schema);
|
||||||
Long stringEntid = report.getEntidForTempId("s");
|
Long stringEntid = report.getEntidForTempId("s");
|
||||||
|
|
||||||
String data = "[\n" +
|
String data = "[\n" +
|
||||||
|
@ -135,13 +163,16 @@ public class FFIIntegrationTest {
|
||||||
" [:db/add \"a\" :foo/uuid #uuid \"550e8400-e29b-41d4-a716-446655440000\"]\n" +
|
" [:db/add \"a\" :foo/uuid #uuid \"550e8400-e29b-41d4-a716-446655440000\"]\n" +
|
||||||
" [:db/add \"b\" :foo/boolean false]\n" +
|
" [:db/add \"b\" :foo/boolean false]\n" +
|
||||||
" [:db/add \"b\" :foo/ref "+ stringEntid +"]\n" +
|
" [:db/add \"b\" :foo/ref "+ stringEntid +"]\n" +
|
||||||
|
" [:db/add \"b\" :foo/keyword :foo/string]\n" +
|
||||||
" [:db/add \"b\" :foo/long 50]\n" +
|
" [:db/add \"b\" :foo/long 50]\n" +
|
||||||
" [:db/add \"b\" :foo/instant #inst \"2018-01-01T11:00:00.000Z\"]\n" +
|
" [:db/add \"b\" :foo/instant #inst \"2018-01-01T11:00:00.000Z\"]\n" +
|
||||||
" [:db/add \"b\" :foo/double 22.46]\n" +
|
" [:db/add \"b\" :foo/double 22.46]\n" +
|
||||||
" [:db/add \"b\" :foo/string \"Silence is worse; all truths that are kept silent become poisonous.\"]\n" +
|
" [:db/add \"b\" :foo/string \"Silence is worse; all truths that are kept silent become poisonous.\"]\n" +
|
||||||
" [:db/add \"b\" :foo/uuid #uuid \"4cb3f828-752d-497a-90c9-b1fd516d5644\"]\n" +
|
" [:db/add \"b\" :foo/uuid #uuid \"4cb3f828-752d-497a-90c9-b1fd516d5644\"]\n" +
|
||||||
" ]";
|
" ]";
|
||||||
return mentat.transact(data);
|
TxReport dataReport = transaction.transact(data);
|
||||||
|
transaction.commit();
|
||||||
|
return new DBSetupResult(report, dataReport);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -168,7 +199,7 @@ public class FFIIntegrationTest {
|
||||||
Mentat mentat = openAndInitializeCitiesStore();
|
Mentat mentat = openAndInitializeCitiesStore();
|
||||||
String query = "[:find ?n . :in ?name :where [(fulltext $ :community/name ?name) [[?e ?n]]]]";
|
String query = "[:find ?n . :in ?name :where [(fulltext $ :community/name ?name) [[?e ?n]]]]";
|
||||||
final Expectation expectation = new Expectation();
|
final Expectation expectation = new Expectation();
|
||||||
mentat.query(query).bindString("?name", "Wallingford").runScalar(new ScalarResultHandler() {
|
mentat.query(query).bind("?name", "Wallingford").run(new ScalarResultHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void handleValue(TypedValue value) {
|
public void handleValue(TypedValue value) {
|
||||||
assertNotNull(value);
|
assertNotNull(value);
|
||||||
|
@ -187,7 +218,7 @@ public class FFIIntegrationTest {
|
||||||
Mentat mentat = openAndInitializeCitiesStore();
|
Mentat mentat = openAndInitializeCitiesStore();
|
||||||
String query = "[:find [?when ...] :where [_ :db/txInstant ?when] :order (asc ?when)]";
|
String query = "[:find [?when ...] :where [_ :db/txInstant ?when] :order (asc ?when)]";
|
||||||
final Expectation expectation = new Expectation();
|
final Expectation expectation = new Expectation();
|
||||||
mentat.query(query).runColl(new CollResultHandler() {
|
mentat.query(query).run(new CollResultHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void handleList(CollResult list) {
|
public void handleList(CollResult list) {
|
||||||
assertNotNull(list);
|
assertNotNull(list);
|
||||||
|
@ -208,7 +239,7 @@ public class FFIIntegrationTest {
|
||||||
Mentat mentat = openAndInitializeCitiesStore();
|
Mentat mentat = openAndInitializeCitiesStore();
|
||||||
String query = "[:find [?when ...] :where [_ :db/txInstant ?when] :order (asc ?when)]";
|
String query = "[:find [?when ...] :where [_ :db/txInstant ?when] :order (asc ?when)]";
|
||||||
final Expectation expectation = new Expectation();
|
final Expectation expectation = new Expectation();
|
||||||
mentat.query(query).runColl(new CollResultHandler() {
|
mentat.query(query).run(new CollResultHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void handleList(CollResult list) {
|
public void handleList(CollResult list) {
|
||||||
assertNotNull(list);
|
assertNotNull(list);
|
||||||
|
@ -234,7 +265,7 @@ public class FFIIntegrationTest {
|
||||||
" [?c :community/type :community.type/website]\n" +
|
" [?c :community/type :community.type/website]\n" +
|
||||||
" [(fulltext $ :community/category \"food\") [[?c ?cat]]]]";
|
" [(fulltext $ :community/category \"food\") [[?c ?cat]]]]";
|
||||||
final Expectation expectation = new Expectation();
|
final Expectation expectation = new Expectation();
|
||||||
mentat.query(query).runTuple(new TupleResultHandler() {
|
mentat.query(query).run(new TupleResultHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void handleRow(TupleResult row) {
|
public void handleRow(TupleResult row) {
|
||||||
assertNotNull(row);
|
assertNotNull(row);
|
||||||
|
@ -333,11 +364,11 @@ public class FFIIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
public void bindingLongValueSucceeds() throws InterruptedException {
|
public void bindingLongValueSucceeds() throws InterruptedException {
|
||||||
Mentat mentat = new Mentat();
|
Mentat mentat = new Mentat();
|
||||||
TxReport report = this.populateWithTypesSchema(mentat);
|
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||||
final Long aEntid = report.getEntidForTempId("a");
|
final Long aEntid = report.getEntidForTempId("a");
|
||||||
String query = "[:find ?e . :in ?long :where [?e :foo/long ?long]]";
|
String query = "[:find ?e . :in ?long :where [?e :foo/long ?long]]";
|
||||||
final Expectation expectation = new Expectation();
|
final Expectation expectation = new Expectation();
|
||||||
mentat.query(query).bindLong("?long", 25).runScalar(new ScalarResultHandler() {
|
mentat.query(query).bind("?long", 25).run(new ScalarResultHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void handleValue(TypedValue value) {
|
public void handleValue(TypedValue value) {
|
||||||
assertNotNull(value);
|
assertNotNull(value);
|
||||||
|
@ -354,12 +385,12 @@ public class FFIIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
public void bindingRefValueSucceeds() throws InterruptedException {
|
public void bindingRefValueSucceeds() throws InterruptedException {
|
||||||
Mentat mentat = new Mentat();
|
Mentat mentat = new Mentat();
|
||||||
TxReport report = this.populateWithTypesSchema(mentat);
|
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||||
long stringEntid = mentat.entIdForAttribute(":foo/string");
|
long stringEntid = mentat.entIdForAttribute(":foo/string");
|
||||||
final Long bEntid = report.getEntidForTempId("b");
|
final Long bEntid = report.getEntidForTempId("b");
|
||||||
String query = "[:find ?e . :in ?ref :where [?e :foo/ref ?ref]]";
|
String query = "[:find ?e . :in ?ref :where [?e :foo/ref ?ref]]";
|
||||||
final Expectation expectation = new Expectation();
|
final Expectation expectation = new Expectation();
|
||||||
mentat.query(query).bindEntidReference("?ref", stringEntid).runScalar(new ScalarResultHandler() {
|
mentat.query(query).bindEntidReference("?ref", stringEntid).run(new ScalarResultHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void handleValue(TypedValue value) {
|
public void handleValue(TypedValue value) {
|
||||||
assertNotNull(value);
|
assertNotNull(value);
|
||||||
|
@ -376,12 +407,12 @@ public class FFIIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
public void bindingRefKwValueSucceeds() throws InterruptedException {
|
public void bindingRefKwValueSucceeds() throws InterruptedException {
|
||||||
Mentat mentat = new Mentat();
|
Mentat mentat = new Mentat();
|
||||||
TxReport report = this.populateWithTypesSchema(mentat);
|
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||||
String refKeyword = ":foo/string";
|
String refKeyword = ":foo/string";
|
||||||
final Long bEntid = report.getEntidForTempId("b");
|
final Long bEntid = report.getEntidForTempId("b");
|
||||||
String query = "[:find ?e . :in ?ref :where [?e :foo/ref ?ref]]";
|
String query = "[:find ?e . :in ?ref :where [?e :foo/ref ?ref]]";
|
||||||
final Expectation expectation = new Expectation();
|
final Expectation expectation = new Expectation();
|
||||||
mentat.query(query).bindKeywordReference("?ref", refKeyword).runScalar(new ScalarResultHandler() {
|
mentat.query(query).bindKeywordReference("?ref", refKeyword).run(new ScalarResultHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void handleValue(TypedValue value) {
|
public void handleValue(TypedValue value) {
|
||||||
assertNotNull(value);
|
assertNotNull(value);
|
||||||
|
@ -398,11 +429,11 @@ public class FFIIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
public void bindingKwValueSucceeds() throws InterruptedException {
|
public void bindingKwValueSucceeds() throws InterruptedException {
|
||||||
Mentat mentat = new Mentat();
|
Mentat mentat = new Mentat();
|
||||||
TxReport report = this.populateWithTypesSchema(mentat);
|
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||||
final Long aEntid = report.getEntidForTempId("a");
|
final Long aEntid = report.getEntidForTempId("a");
|
||||||
String query = "[:find ?e . :in ?kw :where [?e :foo/keyword ?kw]]";
|
String query = "[:find ?e . :in ?kw :where [?e :foo/keyword ?kw]]";
|
||||||
final Expectation expectation = new Expectation();
|
final Expectation expectation = new Expectation();
|
||||||
mentat.query(query).bindKeyword("?kw", ":foo/string").runScalar(new ScalarResultHandler() {
|
mentat.query(query).bindKeyword("?kw", ":foo/string").run(new ScalarResultHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void handleValue(TypedValue value) {
|
public void handleValue(TypedValue value) {
|
||||||
assertNotNull(value);
|
assertNotNull(value);
|
||||||
|
@ -419,13 +450,13 @@ public class FFIIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
public void bindingDateValueSucceeds() throws InterruptedException, ParseException {
|
public void bindingDateValueSucceeds() throws InterruptedException, ParseException {
|
||||||
Mentat mentat = new Mentat();
|
Mentat mentat = new Mentat();
|
||||||
TxReport report = this.populateWithTypesSchema(mentat);
|
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||||
final Long aEntid = report.getEntidForTempId("a");
|
final Long aEntid = report.getEntidForTempId("a");
|
||||||
|
|
||||||
Date date = new Date(1523896758000L);
|
Date date = new Date(1523896758000L);
|
||||||
String query = "[:find [?e ?d] :in ?now :where [?e :foo/instant ?d] [(< ?d ?now)]]";
|
String query = "[:find [?e ?d] :in ?now :where [?e :foo/instant ?d] [(< ?d ?now)]]";
|
||||||
final Expectation expectation = new Expectation();
|
final Expectation expectation = new Expectation();
|
||||||
mentat.query(query).bindDate("?now", date).runTuple(new TupleResultHandler() {
|
mentat.query(query).bind("?now", date).run(new TupleResultHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void handleRow(TupleResult row) {
|
public void handleRow(TupleResult row) {
|
||||||
assertNotNull(row);
|
assertNotNull(row);
|
||||||
|
@ -446,7 +477,7 @@ public class FFIIntegrationTest {
|
||||||
Mentat mentat = this.openAndInitializeCitiesStore();
|
Mentat mentat = this.openAndInitializeCitiesStore();
|
||||||
String query = "[:find ?n . :in ?name :where [(fulltext $ :community/name ?name) [[?e ?n]]]]";
|
String query = "[:find ?n . :in ?name :where [(fulltext $ :community/name ?name) [[?e ?n]]]]";
|
||||||
final Expectation expectation = new Expectation();
|
final Expectation expectation = new Expectation();
|
||||||
mentat.query(query).bindString("?name", "Wallingford").runScalar(new ScalarResultHandler() {
|
mentat.query(query).bind("?name", "Wallingford").run(new ScalarResultHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void handleValue(TypedValue value) {
|
public void handleValue(TypedValue value) {
|
||||||
assertNotNull(value);
|
assertNotNull(value);
|
||||||
|
@ -463,12 +494,12 @@ public class FFIIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
public void bindingUuidValueSucceeds() throws InterruptedException {
|
public void bindingUuidValueSucceeds() throws InterruptedException {
|
||||||
Mentat mentat = new Mentat();
|
Mentat mentat = new Mentat();
|
||||||
TxReport report = this.populateWithTypesSchema(mentat);
|
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||||
final Long aEntid = report.getEntidForTempId("a");
|
final Long aEntid = report.getEntidForTempId("a");
|
||||||
String query = "[:find ?e . :in ?uuid :where [?e :foo/uuid ?uuid]]";
|
String query = "[:find ?e . :in ?uuid :where [?e :foo/uuid ?uuid]]";
|
||||||
UUID uuid = UUID.fromString("550e8400-e29b-41d4-a716-446655440000");
|
UUID uuid = UUID.fromString("550e8400-e29b-41d4-a716-446655440000");
|
||||||
final Expectation expectation = new Expectation();
|
final Expectation expectation = new Expectation();
|
||||||
mentat.query(query).bindUUID("?uuid", uuid).runScalar(new ScalarResultHandler() {
|
mentat.query(query).bind("?uuid", uuid).run(new ScalarResultHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void handleValue(TypedValue value) {
|
public void handleValue(TypedValue value) {
|
||||||
assertNotNull(value);
|
assertNotNull(value);
|
||||||
|
@ -485,11 +516,11 @@ public class FFIIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
public void bindingBooleanValueSucceeds() throws InterruptedException {
|
public void bindingBooleanValueSucceeds() throws InterruptedException {
|
||||||
Mentat mentat = new Mentat();
|
Mentat mentat = new Mentat();
|
||||||
TxReport report = this.populateWithTypesSchema(mentat);
|
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||||
final Long aEntid = report.getEntidForTempId("a");
|
final Long aEntid = report.getEntidForTempId("a");
|
||||||
String query = "[:find ?e . :in ?bool :where [?e :foo/boolean ?bool]]";
|
String query = "[:find ?e . :in ?bool :where [?e :foo/boolean ?bool]]";
|
||||||
final Expectation expectation = new Expectation();
|
final Expectation expectation = new Expectation();
|
||||||
mentat.query(query).bindBoolean("?bool", true).runScalar(new ScalarResultHandler() {
|
mentat.query(query).bind("?bool", true).run(new ScalarResultHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void handleValue(TypedValue value) {
|
public void handleValue(TypedValue value) {
|
||||||
assertNotNull(value);
|
assertNotNull(value);
|
||||||
|
@ -507,11 +538,11 @@ public class FFIIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
public void bindingDoubleValueSucceeds() throws InterruptedException {
|
public void bindingDoubleValueSucceeds() throws InterruptedException {
|
||||||
Mentat mentat = new Mentat();
|
Mentat mentat = new Mentat();
|
||||||
TxReport report = this.populateWithTypesSchema(mentat);
|
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||||
final Long aEntid = report.getEntidForTempId("a");
|
final Long aEntid = report.getEntidForTempId("a");
|
||||||
String query = "[:find ?e . :in ?double :where [?e :foo/double ?double]]";
|
String query = "[:find ?e . :in ?double :where [?e :foo/double ?double]]";
|
||||||
final Expectation expectation = new Expectation();
|
final Expectation expectation = new Expectation();
|
||||||
mentat.query(query).bindDouble("?double", 11.23).runScalar(new ScalarResultHandler() {
|
mentat.query(query).bind("?double", 11.23).run(new ScalarResultHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void handleValue(TypedValue value) {
|
public void handleValue(TypedValue value) {
|
||||||
assertNotNull(value);
|
assertNotNull(value);
|
||||||
|
@ -528,11 +559,11 @@ public class FFIIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
public void typedValueConvertsToLong() throws InterruptedException {
|
public void typedValueConvertsToLong() throws InterruptedException {
|
||||||
Mentat mentat = new Mentat();
|
Mentat mentat = new Mentat();
|
||||||
TxReport report = this.populateWithTypesSchema(mentat);
|
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||||
final Long aEntid = report.getEntidForTempId("a");
|
final Long aEntid = report.getEntidForTempId("a");
|
||||||
String query = "[:find ?v . :in ?e :where [?e :foo/long ?v]]";
|
String query = "[:find ?v . :in ?e :where [?e :foo/long ?v]]";
|
||||||
final Expectation expectation = new Expectation();
|
final Expectation expectation = new Expectation();
|
||||||
mentat.query(query).bindEntidReference("?e", aEntid).runScalar(new ScalarResultHandler() {
|
mentat.query(query).bindEntidReference("?e", aEntid).run(new ScalarResultHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void handleValue(TypedValue value) {
|
public void handleValue(TypedValue value) {
|
||||||
assertNotNull(value);
|
assertNotNull(value);
|
||||||
|
@ -550,11 +581,11 @@ public class FFIIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
public void typedValueConvertsToRef() throws InterruptedException {
|
public void typedValueConvertsToRef() throws InterruptedException {
|
||||||
Mentat mentat = new Mentat();
|
Mentat mentat = new Mentat();
|
||||||
TxReport report = this.populateWithTypesSchema(mentat);
|
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||||
final Long aEntid = report.getEntidForTempId("a");
|
final Long aEntid = report.getEntidForTempId("a");
|
||||||
String query = "[:find ?e . :where [?e :foo/long 25]]";
|
String query = "[:find ?e . :where [?e :foo/long 25]]";
|
||||||
final Expectation expectation = new Expectation();
|
final Expectation expectation = new Expectation();
|
||||||
mentat.query(query).runScalar(new ScalarResultHandler() {
|
mentat.query(query).run(new ScalarResultHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void handleValue(TypedValue value) {
|
public void handleValue(TypedValue value) {
|
||||||
assertNotNull(value);
|
assertNotNull(value);
|
||||||
|
@ -572,11 +603,11 @@ public class FFIIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
public void typedValueConvertsToKeyword() throws InterruptedException {
|
public void typedValueConvertsToKeyword() throws InterruptedException {
|
||||||
Mentat mentat = new Mentat();
|
Mentat mentat = new Mentat();
|
||||||
TxReport report = this.populateWithTypesSchema(mentat);
|
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||||
final Long aEntid = report.getEntidForTempId("a");
|
final Long aEntid = report.getEntidForTempId("a");
|
||||||
String query = "[:find ?v . :in ?e :where [?e :foo/keyword ?v]]";
|
String query = "[:find ?v . :in ?e :where [?e :foo/keyword ?v]]";
|
||||||
final Expectation expectation = new Expectation();
|
final Expectation expectation = new Expectation();
|
||||||
mentat.query(query).bindEntidReference("?e", aEntid).runScalar(new ScalarResultHandler() {
|
mentat.query(query).bindEntidReference("?e", aEntid).run(new ScalarResultHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void handleValue(TypedValue value) {
|
public void handleValue(TypedValue value) {
|
||||||
assertNotNull(value);
|
assertNotNull(value);
|
||||||
|
@ -594,11 +625,11 @@ public class FFIIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
public void typedValueConvertsToBoolean() throws InterruptedException {
|
public void typedValueConvertsToBoolean() throws InterruptedException {
|
||||||
Mentat mentat = new Mentat();
|
Mentat mentat = new Mentat();
|
||||||
TxReport report = this.populateWithTypesSchema(mentat);
|
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||||
final Long aEntid = report.getEntidForTempId("a");
|
final Long aEntid = report.getEntidForTempId("a");
|
||||||
String query = "[:find ?v . :in ?e :where [?e :foo/boolean ?v]]";
|
String query = "[:find ?v . :in ?e :where [?e :foo/boolean ?v]]";
|
||||||
final Expectation expectation = new Expectation();
|
final Expectation expectation = new Expectation();
|
||||||
mentat.query(query).bindEntidReference("?e", aEntid).runScalar(new ScalarResultHandler() {
|
mentat.query(query).bindEntidReference("?e", aEntid).run(new ScalarResultHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void handleValue(TypedValue value) {
|
public void handleValue(TypedValue value) {
|
||||||
assertNotNull(value);
|
assertNotNull(value);
|
||||||
|
@ -616,11 +647,11 @@ public class FFIIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
public void typedValueConvertsToDouble() throws InterruptedException {
|
public void typedValueConvertsToDouble() throws InterruptedException {
|
||||||
Mentat mentat = new Mentat();
|
Mentat mentat = new Mentat();
|
||||||
TxReport report = this.populateWithTypesSchema(mentat);
|
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||||
final Long aEntid = report.getEntidForTempId("a");
|
final Long aEntid = report.getEntidForTempId("a");
|
||||||
String query = "[:find ?v . :in ?e :where [?e :foo/double ?v]]";
|
String query = "[:find ?v . :in ?e :where [?e :foo/double ?v]]";
|
||||||
final Expectation expectation = new Expectation();
|
final Expectation expectation = new Expectation();
|
||||||
mentat.query(query).bindEntidReference("?e", aEntid).runScalar(new ScalarResultHandler() {
|
mentat.query(query).bindEntidReference("?e", aEntid).run(new ScalarResultHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void handleValue(TypedValue value) {
|
public void handleValue(TypedValue value) {
|
||||||
assertNotNull(value);
|
assertNotNull(value);
|
||||||
|
@ -638,14 +669,14 @@ public class FFIIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
public void typedValueConvertsToDate() throws InterruptedException, ParseException {
|
public void typedValueConvertsToDate() throws InterruptedException, ParseException {
|
||||||
Mentat mentat = new Mentat();
|
Mentat mentat = new Mentat();
|
||||||
TxReport report = this.populateWithTypesSchema(mentat);
|
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||||
final Long aEntid = report.getEntidForTempId("a");
|
final Long aEntid = report.getEntidForTempId("a");
|
||||||
String query = "[:find ?v . :in ?e :where [?e :foo/instant ?v]]";
|
String query = "[:find ?v . :in ?e :where [?e :foo/instant ?v]]";
|
||||||
final Expectation expectation = new Expectation();
|
final Expectation expectation = new Expectation();
|
||||||
DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ", Locale.ENGLISH);
|
DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ", Locale.ENGLISH);
|
||||||
format.parse("2017-01-01T11:00:00+00:00");
|
format.parse("2017-01-01T11:00:00+00:00");
|
||||||
final Calendar expectedDate = format.getCalendar();
|
final Calendar expectedDate = format.getCalendar();
|
||||||
mentat.query(query).bindEntidReference("?e", aEntid).runScalar(new ScalarResultHandler() {
|
mentat.query(query).bindEntidReference("?e", aEntid).run(new ScalarResultHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void handleValue(TypedValue value) {
|
public void handleValue(TypedValue value) {
|
||||||
assertNotNull(value);
|
assertNotNull(value);
|
||||||
|
@ -663,11 +694,11 @@ public class FFIIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
public void typedValueConvertsToString() throws InterruptedException {
|
public void typedValueConvertsToString() throws InterruptedException {
|
||||||
Mentat mentat = new Mentat();
|
Mentat mentat = new Mentat();
|
||||||
TxReport report = this.populateWithTypesSchema(mentat);
|
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||||
final Long aEntid = report.getEntidForTempId("a");
|
final Long aEntid = report.getEntidForTempId("a");
|
||||||
String query = "[:find ?v . :in ?e :where [?e :foo/string ?v]]";
|
String query = "[:find ?v . :in ?e :where [?e :foo/string ?v]]";
|
||||||
final Expectation expectation = new Expectation();
|
final Expectation expectation = new Expectation();
|
||||||
mentat.query(query).bindEntidReference("?e", aEntid).runScalar(new ScalarResultHandler() {
|
mentat.query(query).bindEntidReference("?e", aEntid).run(new ScalarResultHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void handleValue(TypedValue value) {
|
public void handleValue(TypedValue value) {
|
||||||
assertNotNull(value);
|
assertNotNull(value);
|
||||||
|
@ -685,12 +716,12 @@ public class FFIIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
public void typedValueConvertsToUUID() throws InterruptedException {
|
public void typedValueConvertsToUUID() throws InterruptedException {
|
||||||
Mentat mentat = new Mentat();
|
Mentat mentat = new Mentat();
|
||||||
TxReport report = this.populateWithTypesSchema(mentat);
|
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||||
final Long aEntid = report.getEntidForTempId("a");
|
final Long aEntid = report.getEntidForTempId("a");
|
||||||
String query = "[:find ?v . :in ?e :where [?e :foo/uuid ?v]]";
|
String query = "[:find ?v . :in ?e :where [?e :foo/uuid ?v]]";
|
||||||
final UUID expectedUUID = UUID.fromString("550e8400-e29b-41d4-a716-446655440000");
|
final UUID expectedUUID = UUID.fromString("550e8400-e29b-41d4-a716-446655440000");
|
||||||
final Expectation expectation = new Expectation();
|
final Expectation expectation = new Expectation();
|
||||||
mentat.query(query).bindEntidReference("?e", aEntid).runScalar(new ScalarResultHandler() {
|
mentat.query(query).bindEntidReference("?e", aEntid).run(new ScalarResultHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void handleValue(TypedValue value) {
|
public void handleValue(TypedValue value) {
|
||||||
assertNotNull(value);
|
assertNotNull(value);
|
||||||
|
@ -708,7 +739,7 @@ public class FFIIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
public void valueForAttributeOfEntitySucceeds() throws InterruptedException {
|
public void valueForAttributeOfEntitySucceeds() throws InterruptedException {
|
||||||
Mentat mentat = new Mentat();
|
Mentat mentat = new Mentat();
|
||||||
TxReport report = this.populateWithTypesSchema(mentat);
|
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||||
final Long aEntid = report.getEntidForTempId("a");
|
final Long aEntid = report.getEntidForTempId("a");
|
||||||
TypedValue value = mentat.valueForAttributeOfEntity(":foo/long", aEntid);
|
TypedValue value = mentat.valueForAttributeOfEntity(":foo/long", aEntid);
|
||||||
assertNotNull(value);
|
assertNotNull(value);
|
||||||
|
@ -722,4 +753,569 @@ public class FFIIntegrationTest {
|
||||||
long entid = mentat.entIdForAttribute(":foo/long");
|
long entid = mentat.entIdForAttribute(":foo/long");
|
||||||
assertEquals(65540, entid);
|
assertEquals(65540, entid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInProgressTransact() {
|
||||||
|
Mentat mentat = new Mentat();
|
||||||
|
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||||
|
assertNotNull(report);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInProgressRollback() {
|
||||||
|
Mentat mentat = new Mentat();
|
||||||
|
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||||
|
assertNotNull(report);
|
||||||
|
long aEntid = report.getEntidForTempId("a");
|
||||||
|
TypedValue preLongValue = mentat.valueForAttributeOfEntity(":foo/long", aEntid);
|
||||||
|
assertEquals(25, preLongValue.asLong().longValue());
|
||||||
|
|
||||||
|
InProgress inProgress = mentat.beginTransaction();
|
||||||
|
report = inProgress.transact("[[:db/add "+ aEntid +" :foo/long 22]]");
|
||||||
|
assertNotNull(report);
|
||||||
|
inProgress.rollback();
|
||||||
|
|
||||||
|
TypedValue postLongValue = mentat.valueForAttributeOfEntity(":foo/long", aEntid);
|
||||||
|
assertEquals(25, postLongValue.asLong().longValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInProgressEntityBuilder() throws InterruptedException {
|
||||||
|
Mentat mentat = new Mentat();
|
||||||
|
DBSetupResult reports = this.populateWithTypesSchema(mentat);
|
||||||
|
long bEntid = reports.dataReport.getEntidForTempId("b");
|
||||||
|
final long longEntid = reports.schemaReport.getEntidForTempId("l");
|
||||||
|
final long stringEntid = reports.schemaReport.getEntidForTempId("s");
|
||||||
|
|
||||||
|
// test that the values are as expected
|
||||||
|
String query = "[:find [?b ?i ?u ?l ?d ?s ?k ?r]\n" +
|
||||||
|
" :in ?e\n" +
|
||||||
|
" :where [?e :foo/boolean ?b]\n" +
|
||||||
|
" [?e :foo/instant ?i]\n" +
|
||||||
|
" [?e :foo/uuid ?u]\n" +
|
||||||
|
" [?e :foo/long ?l]\n" +
|
||||||
|
" [?e :foo/double ?d]\n" +
|
||||||
|
" [?e :foo/string ?s]\n" +
|
||||||
|
" [?e :foo/keyword ?k]\n" +
|
||||||
|
" [?e :foo/ref ?r]]";
|
||||||
|
|
||||||
|
final Expectation expectation1 = new Expectation();
|
||||||
|
mentat.query(query).bindEntidReference("?e", bEntid).run(new TupleResultHandler() {
|
||||||
|
@Override
|
||||||
|
public void handleRow(TupleResult row) {
|
||||||
|
assertNotNull(row);
|
||||||
|
assertEquals(false, row.asBool(0));
|
||||||
|
assertEquals(new Date(1514804400000l), row.asDate(1));
|
||||||
|
assertEquals(UUID.fromString("4cb3f828-752d-497a-90c9-b1fd516d5644"), row.asUUID(2));
|
||||||
|
assertEquals(50, row.asLong(3).longValue());
|
||||||
|
assertEquals(new Double(22.46), row.asDouble(4));
|
||||||
|
assertEquals("Silence is worse; all truths that are kept silent become poisonous.", row.asString(5));
|
||||||
|
assertEquals(":foo/string", row.asKeyword(6));
|
||||||
|
assertEquals(stringEntid, row.asEntid(7).longValue());
|
||||||
|
expectation1.fulfill();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
synchronized (expectation1) {
|
||||||
|
expectation1.wait(1000);
|
||||||
|
}
|
||||||
|
assertTrue(expectation1.isFulfilled);
|
||||||
|
|
||||||
|
InProgressBuilder builder = mentat.entityBuilder();
|
||||||
|
builder.add(bEntid, ":foo/boolean", true);
|
||||||
|
final Date newDate = new Date(1524743301000l);
|
||||||
|
builder.add(bEntid, ":foo/instant", newDate);
|
||||||
|
final UUID newUUID = UUID.randomUUID();
|
||||||
|
builder.add(bEntid, ":foo/uuid", newUUID);
|
||||||
|
builder.add(bEntid, ":foo/long", 75);
|
||||||
|
builder.add(bEntid, ":foo/double", 81.3);
|
||||||
|
builder.add(bEntid, ":foo/string", "Become who you are!");
|
||||||
|
builder.addKeyword(bEntid, ":foo/keyword", ":foo/long");
|
||||||
|
builder.addRef(bEntid, ":foo/ref", longEntid);
|
||||||
|
builder.commit();
|
||||||
|
|
||||||
|
|
||||||
|
final Expectation expectation2 = new Expectation();
|
||||||
|
mentat.query(query).bindEntidReference("?e", bEntid).run(new TupleResultHandler() {
|
||||||
|
@Override
|
||||||
|
public void handleRow(TupleResult row) {
|
||||||
|
assertNotNull(row);
|
||||||
|
assertEquals(true, row.asBool(0));
|
||||||
|
System.out.println(row.asDate(1).getTime());
|
||||||
|
assertEquals(newDate, row.asDate(1));
|
||||||
|
assertEquals(newUUID, row.asUUID(2));
|
||||||
|
assertEquals(75, row.asLong(3).longValue());
|
||||||
|
assertEquals(new Double(81.3), row.asDouble(4));
|
||||||
|
assertEquals("Become who you are!", row.asString(5));
|
||||||
|
assertEquals(":foo/long", row.asKeyword(6));
|
||||||
|
assertEquals(longEntid, row.asEntid(7).longValue());
|
||||||
|
expectation2.fulfill();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
synchronized (expectation2) {
|
||||||
|
expectation2.wait(1000);
|
||||||
|
}
|
||||||
|
assertTrue(expectation2.isFulfilled);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEntityBuilderForEntid() throws InterruptedException {
|
||||||
|
Mentat mentat = new Mentat();
|
||||||
|
DBSetupResult reports = this.populateWithTypesSchema(mentat);
|
||||||
|
long bEntid = reports.dataReport.getEntidForTempId("b");
|
||||||
|
final long longEntid = reports.schemaReport.getEntidForTempId("l");
|
||||||
|
final long stringEntid = reports.schemaReport.getEntidForTempId("s");
|
||||||
|
|
||||||
|
// test that the values are as expected
|
||||||
|
String query = "[:find [?b ?i ?u ?l ?d ?s ?k ?r]\n" +
|
||||||
|
" :in ?e\n" +
|
||||||
|
" :where [?e :foo/boolean ?b]\n" +
|
||||||
|
" [?e :foo/instant ?i]\n" +
|
||||||
|
" [?e :foo/uuid ?u]\n" +
|
||||||
|
" [?e :foo/long ?l]\n" +
|
||||||
|
" [?e :foo/double ?d]\n" +
|
||||||
|
" [?e :foo/string ?s]\n" +
|
||||||
|
" [?e :foo/keyword ?k]\n" +
|
||||||
|
" [?e :foo/ref ?r]]";
|
||||||
|
|
||||||
|
final Expectation expectation1 = new Expectation();
|
||||||
|
mentat.query(query).bindEntidReference("?e", bEntid).run(new TupleResultHandler() {
|
||||||
|
@Override
|
||||||
|
public void handleRow(TupleResult row) {
|
||||||
|
assertNotNull(row);
|
||||||
|
assertEquals(false, row.asBool(0));
|
||||||
|
assertEquals(new Date(1514804400000l), row.asDate(1));
|
||||||
|
assertEquals(UUID.fromString("4cb3f828-752d-497a-90c9-b1fd516d5644"), row.asUUID(2));
|
||||||
|
assertEquals(50, row.asLong(3).longValue());
|
||||||
|
assertEquals(new Double(22.46), row.asDouble(4));
|
||||||
|
assertEquals("Silence is worse; all truths that are kept silent become poisonous.", row.asString(5));
|
||||||
|
assertEquals(":foo/string", row.asKeyword(6));
|
||||||
|
assertEquals(stringEntid, row.asEntid(7).longValue());
|
||||||
|
expectation1.fulfill();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
synchronized (expectation1) {
|
||||||
|
expectation1.wait(1000);
|
||||||
|
}
|
||||||
|
assertTrue(expectation1.isFulfilled);
|
||||||
|
|
||||||
|
EntityBuilder builder = mentat.entityBuilder(bEntid);
|
||||||
|
builder.add(":foo/boolean", true);
|
||||||
|
final Date newDate = new Date(1524743301000l);
|
||||||
|
builder.add(":foo/instant", newDate);
|
||||||
|
final UUID newUUID = UUID.randomUUID();
|
||||||
|
builder.add(":foo/uuid", newUUID);
|
||||||
|
builder.add(":foo/long", 75);
|
||||||
|
builder.add(":foo/double", 81.3);
|
||||||
|
builder.add(":foo/string", "Become who you are!");
|
||||||
|
builder.addKeyword(":foo/keyword", ":foo/long");
|
||||||
|
builder.addRef(":foo/ref", longEntid);
|
||||||
|
builder.commit();
|
||||||
|
|
||||||
|
|
||||||
|
final Expectation expectation2 = new Expectation();
|
||||||
|
mentat.query(query).bindEntidReference("?e", bEntid).run(new TupleResultHandler() {
|
||||||
|
@Override
|
||||||
|
public void handleRow(TupleResult row) {
|
||||||
|
assertNotNull(row);
|
||||||
|
assertEquals(true, row.asBool(0));
|
||||||
|
System.out.println(row.asDate(1).getTime());
|
||||||
|
assertEquals(newDate, row.asDate(1));
|
||||||
|
assertEquals(newUUID, row.asUUID(2));
|
||||||
|
assertEquals(75, row.asLong(3).longValue());
|
||||||
|
assertEquals(new Double(81.3), row.asDouble(4));
|
||||||
|
assertEquals("Become who you are!", row.asString(5));
|
||||||
|
assertEquals(":foo/long", row.asKeyword(6));
|
||||||
|
assertEquals(longEntid, row.asEntid(7).longValue());
|
||||||
|
expectation2.fulfill();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
synchronized (expectation2) {
|
||||||
|
expectation2.wait(1000);
|
||||||
|
}
|
||||||
|
assertTrue(expectation2.isFulfilled);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEntityBuilderForTempid() throws InterruptedException {
|
||||||
|
Mentat mentat = new Mentat();
|
||||||
|
DBSetupResult reports = this.populateWithTypesSchema(mentat);
|
||||||
|
final long longEntid = reports.schemaReport.getEntidForTempId("l");
|
||||||
|
|
||||||
|
EntityBuilder builder = mentat.entityBuilder("c");
|
||||||
|
builder.add(":foo/boolean", true);
|
||||||
|
final Date newDate = new Date(1524743301000l);
|
||||||
|
builder.add(":foo/instant", newDate);
|
||||||
|
final UUID newUUID = UUID.randomUUID();
|
||||||
|
builder.add(":foo/uuid", newUUID);
|
||||||
|
builder.add(":foo/long", 75);
|
||||||
|
builder.add(":foo/double", 81.3);
|
||||||
|
builder.add(":foo/string", "Become who you are!");
|
||||||
|
builder.addKeyword(":foo/keyword", ":foo/long");
|
||||||
|
builder.addRef(":foo/ref", longEntid);
|
||||||
|
TxReport report = builder.commit();
|
||||||
|
long cEntid = report.getEntidForTempId("c");
|
||||||
|
|
||||||
|
// test that the values are as expected
|
||||||
|
String query = "[:find [?b ?i ?u ?l ?d ?s ?k ?r]\n" +
|
||||||
|
" :in ?e\n" +
|
||||||
|
" :where [?e :foo/boolean ?b]\n" +
|
||||||
|
" [?e :foo/instant ?i]\n" +
|
||||||
|
" [?e :foo/uuid ?u]\n" +
|
||||||
|
" [?e :foo/long ?l]\n" +
|
||||||
|
" [?e :foo/double ?d]\n" +
|
||||||
|
" [?e :foo/string ?s]\n" +
|
||||||
|
" [?e :foo/keyword ?k]\n" +
|
||||||
|
" [?e :foo/ref ?r]]";
|
||||||
|
|
||||||
|
final Expectation expectation = new Expectation();
|
||||||
|
mentat.query(query).bindEntidReference("?e", cEntid).run(new TupleResultHandler() {
|
||||||
|
@Override
|
||||||
|
public void handleRow(TupleResult row) {
|
||||||
|
assertNotNull(row);
|
||||||
|
assertEquals(true, row.asBool(0));
|
||||||
|
System.out.println(row.asDate(1).getTime());
|
||||||
|
assertEquals(newDate, row.asDate(1));
|
||||||
|
assertEquals(newUUID, row.asUUID(2));
|
||||||
|
assertEquals(75, row.asLong(3).longValue());
|
||||||
|
assertEquals(new Double(81.3), row.asDouble(4));
|
||||||
|
assertEquals("Become who you are!", row.asString(5));
|
||||||
|
assertEquals(":foo/long", row.asKeyword(6));
|
||||||
|
assertEquals(longEntid, row.asEntid(7).longValue());
|
||||||
|
expectation.fulfill();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
synchronized (expectation) {
|
||||||
|
expectation.wait(1000);
|
||||||
|
}
|
||||||
|
assertTrue(expectation.isFulfilled);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInProgressBuilderTransact() throws InterruptedException {
|
||||||
|
Mentat mentat = new Mentat();
|
||||||
|
DBSetupResult reports = this.populateWithTypesSchema(mentat);
|
||||||
|
long aEntid = reports.dataReport.getEntidForTempId("a");
|
||||||
|
long bEntid = reports.dataReport.getEntidForTempId("b");
|
||||||
|
final long longEntid = reports.schemaReport.getEntidForTempId("l");
|
||||||
|
InProgressBuilder builder = mentat.entityBuilder();
|
||||||
|
builder.add(bEntid, ":foo/boolean", true);
|
||||||
|
final Date newDate = new Date(1524743301000l);
|
||||||
|
builder.add(bEntid, ":foo/instant", newDate);
|
||||||
|
final UUID newUUID = UUID.randomUUID();
|
||||||
|
builder.add(bEntid, ":foo/uuid", newUUID);
|
||||||
|
builder.add(bEntid, ":foo/long", 75);
|
||||||
|
builder.add(bEntid, ":foo/double", 81.3);
|
||||||
|
builder.add(bEntid, ":foo/string", "Become who you are!");
|
||||||
|
builder.addKeyword(bEntid, ":foo/keyword", ":foo/long");
|
||||||
|
builder.addRef(bEntid, ":foo/ref", longEntid);
|
||||||
|
InProgressTransactionResult result = builder.transact();
|
||||||
|
assertNotNull(result);
|
||||||
|
assertNotNull(result.getInProgress());
|
||||||
|
assertNotNull(result.getReport());
|
||||||
|
result.getInProgress().transact("[[:db/add "+ aEntid +" :foo/long 22]]");
|
||||||
|
result.getInProgress().commit();
|
||||||
|
|
||||||
|
// test that the values are as expected
|
||||||
|
String query = "[:find [?b ?i ?u ?l ?d ?s ?k ?r]\n" +
|
||||||
|
" :in ?e\n" +
|
||||||
|
" :where [?e :foo/boolean ?b]\n" +
|
||||||
|
" [?e :foo/instant ?i]\n" +
|
||||||
|
" [?e :foo/uuid ?u]\n" +
|
||||||
|
" [?e :foo/long ?l]\n" +
|
||||||
|
" [?e :foo/double ?d]\n" +
|
||||||
|
" [?e :foo/string ?s]\n" +
|
||||||
|
" [?e :foo/keyword ?k]\n" +
|
||||||
|
" [?e :foo/ref ?r]]";
|
||||||
|
|
||||||
|
final Expectation expectation = new Expectation();
|
||||||
|
mentat.query(query).bindEntidReference("?e", bEntid).run(new TupleResultHandler() {
|
||||||
|
@Override
|
||||||
|
public void handleRow(TupleResult row) {
|
||||||
|
assertNotNull(row);
|
||||||
|
assertEquals(true, row.asBool(0));
|
||||||
|
System.out.println(row.asDate(1).getTime());
|
||||||
|
assertEquals(newDate, row.asDate(1));
|
||||||
|
assertEquals(newUUID, row.asUUID(2));
|
||||||
|
assertEquals(75, row.asLong(3).longValue());
|
||||||
|
assertEquals(new Double(81.3), row.asDouble(4));
|
||||||
|
assertEquals("Become who you are!", row.asString(5));
|
||||||
|
assertEquals(":foo/long", row.asKeyword(6));
|
||||||
|
assertEquals(longEntid, row.asEntid(7).longValue());
|
||||||
|
expectation.fulfill();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
synchronized (expectation) {
|
||||||
|
expectation.wait(1000);
|
||||||
|
}
|
||||||
|
assertTrue(expectation.isFulfilled);
|
||||||
|
|
||||||
|
TypedValue longValue = mentat.valueForAttributeOfEntity(":foo/long", aEntid);
|
||||||
|
assertEquals(22, longValue.asLong().longValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEntityBuilderTransact() throws InterruptedException {
|
||||||
|
Mentat mentat = new Mentat();
|
||||||
|
DBSetupResult reports = this.populateWithTypesSchema(mentat);
|
||||||
|
long aEntid = reports.dataReport.getEntidForTempId("a");
|
||||||
|
long bEntid = reports.dataReport.getEntidForTempId("b");
|
||||||
|
final long longEntid = reports.schemaReport.getEntidForTempId("l");
|
||||||
|
|
||||||
|
EntityBuilder builder = mentat.entityBuilder(bEntid);
|
||||||
|
builder.add(":foo/boolean", true);
|
||||||
|
final Date newDate = new Date(1524743301000l);
|
||||||
|
builder.add(":foo/instant", newDate);
|
||||||
|
final UUID newUUID = UUID.randomUUID();
|
||||||
|
builder.add(":foo/uuid", newUUID);
|
||||||
|
builder.add(":foo/long", 75);
|
||||||
|
builder.add(":foo/double", 81.3);
|
||||||
|
builder.add(":foo/string", "Become who you are!");
|
||||||
|
builder.addKeyword(":foo/keyword", ":foo/long");
|
||||||
|
builder.addRef(":foo/ref", longEntid);
|
||||||
|
InProgressTransactionResult result = builder.transact();
|
||||||
|
assertNotNull(result);
|
||||||
|
assertNotNull(result.getInProgress());
|
||||||
|
assertNotNull(result.getReport());
|
||||||
|
result.getInProgress().transact("[[:db/add "+ aEntid +" :foo/long 22]]");
|
||||||
|
result.getInProgress().commit();
|
||||||
|
|
||||||
|
// test that the values are as expected
|
||||||
|
String query = "[:find [?b ?i ?u ?l ?d ?s ?k ?r]\n" +
|
||||||
|
" :in ?e\n" +
|
||||||
|
" :where [?e :foo/boolean ?b]\n" +
|
||||||
|
" [?e :foo/instant ?i]\n" +
|
||||||
|
" [?e :foo/uuid ?u]\n" +
|
||||||
|
" [?e :foo/long ?l]\n" +
|
||||||
|
" [?e :foo/double ?d]\n" +
|
||||||
|
" [?e :foo/string ?s]\n" +
|
||||||
|
" [?e :foo/keyword ?k]\n" +
|
||||||
|
" [?e :foo/ref ?r]]";
|
||||||
|
|
||||||
|
final Expectation expectation = new Expectation();
|
||||||
|
mentat.query(query).bindEntidReference("?e", bEntid).run(new TupleResultHandler() {
|
||||||
|
@Override
|
||||||
|
public void handleRow(TupleResult row) {
|
||||||
|
assertNotNull(row);
|
||||||
|
assertEquals(true, row.asBool(0));
|
||||||
|
System.out.println(row.asDate(1).getTime());
|
||||||
|
assertEquals(newDate, row.asDate(1));
|
||||||
|
assertEquals(newUUID, row.asUUID(2));
|
||||||
|
assertEquals(75, row.asLong(3).longValue());
|
||||||
|
assertEquals(new Double(81.3), row.asDouble(4));
|
||||||
|
assertEquals("Become who you are!", row.asString(5));
|
||||||
|
assertEquals(":foo/long", row.asKeyword(6));
|
||||||
|
assertEquals(longEntid, row.asEntid(7).longValue());
|
||||||
|
expectation.fulfill();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
synchronized (expectation) {
|
||||||
|
expectation.wait(1000);
|
||||||
|
}
|
||||||
|
assertTrue(expectation.isFulfilled);
|
||||||
|
|
||||||
|
TypedValue longValue = mentat.valueForAttributeOfEntity(":foo/long", aEntid);
|
||||||
|
assertEquals(22, longValue.asLong().longValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEntityBuilderRetract() throws InterruptedException {
|
||||||
|
Mentat mentat = new Mentat();
|
||||||
|
DBSetupResult reports = this.populateWithTypesSchema(mentat);
|
||||||
|
long bEntid = reports.dataReport.getEntidForTempId("b");
|
||||||
|
final long longEntid = reports.schemaReport.getEntidForTempId("l");
|
||||||
|
final long stringEntid = reports.schemaReport.getEntidForTempId("s");
|
||||||
|
|
||||||
|
// test that the values are as expected
|
||||||
|
String query = "[:find [?b ?i ?u ?l ?d ?s ?k ?r]\n" +
|
||||||
|
" :in ?e\n" +
|
||||||
|
" :where [?e :foo/boolean ?b]\n" +
|
||||||
|
" [?e :foo/instant ?i]\n" +
|
||||||
|
" [?e :foo/uuid ?u]\n" +
|
||||||
|
" [?e :foo/long ?l]\n" +
|
||||||
|
" [?e :foo/double ?d]\n" +
|
||||||
|
" [?e :foo/string ?s]\n" +
|
||||||
|
" [?e :foo/keyword ?k]\n" +
|
||||||
|
" [?e :foo/ref ?r]]";
|
||||||
|
|
||||||
|
final Expectation expectation1 = new Expectation();
|
||||||
|
final Date previousDate = new Date(1514804400000l);
|
||||||
|
final UUID previousUuid = UUID.fromString("4cb3f828-752d-497a-90c9-b1fd516d5644");
|
||||||
|
mentat.query(query).bindEntidReference("?e", bEntid).run(new TupleResultHandler() {
|
||||||
|
@Override
|
||||||
|
public void handleRow(TupleResult row) {
|
||||||
|
assertNotNull(row);
|
||||||
|
assertEquals(false, row.asBool(0));
|
||||||
|
assertEquals(previousDate, row.asDate(1));
|
||||||
|
assertEquals(previousUuid, row.asUUID(2));
|
||||||
|
assertEquals(50, row.asLong(3).longValue());
|
||||||
|
assertEquals(new Double(22.46), row.asDouble(4));
|
||||||
|
assertEquals("Silence is worse; all truths that are kept silent become poisonous.", row.asString(5));
|
||||||
|
assertEquals(":foo/string", row.asKeyword(6));
|
||||||
|
assertEquals(stringEntid, row.asEntid(7).longValue());
|
||||||
|
expectation1.fulfill();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
synchronized (expectation1) {
|
||||||
|
expectation1.wait(1000);
|
||||||
|
}
|
||||||
|
assertTrue(expectation1.isFulfilled);
|
||||||
|
|
||||||
|
EntityBuilder builder = mentat.entityBuilder(bEntid);
|
||||||
|
builder.retract(":foo/boolean", false);
|
||||||
|
builder.retract(":foo/instant", previousDate);
|
||||||
|
builder.retract(":foo/uuid", previousUuid);
|
||||||
|
builder.retract(":foo/long", 50);
|
||||||
|
builder.retract(":foo/double", 22.46);
|
||||||
|
builder.retract(":foo/string", "Silence is worse; all truths that are kept silent become poisonous.");
|
||||||
|
builder.retractKeyword(":foo/keyword", ":foo/string");
|
||||||
|
builder.retractRef(":foo/ref", stringEntid);
|
||||||
|
builder.commit();
|
||||||
|
|
||||||
|
final Expectation expectation2 = new Expectation();
|
||||||
|
mentat.query(query).bindEntidReference("?e", bEntid).run(new TupleResultHandler() {
|
||||||
|
@Override
|
||||||
|
public void handleRow(TupleResult row) {
|
||||||
|
assertNull(row);
|
||||||
|
expectation2.fulfill();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
synchronized (expectation2) {
|
||||||
|
expectation2.wait(1000);
|
||||||
|
}
|
||||||
|
assertTrue(expectation2.isFulfilled);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInProgressBuilderRetract() throws InterruptedException {
|
||||||
|
Mentat mentat = new Mentat();
|
||||||
|
DBSetupResult reports = this.populateWithTypesSchema(mentat);
|
||||||
|
long bEntid = reports.dataReport.getEntidForTempId("b");
|
||||||
|
final long longEntid = reports.schemaReport.getEntidForTempId("l");
|
||||||
|
final long stringEntid = reports.schemaReport.getEntidForTempId("s");
|
||||||
|
|
||||||
|
// test that the values are as expected
|
||||||
|
String query = "[:find [?b ?i ?u ?l ?d ?s ?k ?r]\n" +
|
||||||
|
" :in ?e\n" +
|
||||||
|
" :where [?e :foo/boolean ?b]\n" +
|
||||||
|
" [?e :foo/instant ?i]\n" +
|
||||||
|
" [?e :foo/uuid ?u]\n" +
|
||||||
|
" [?e :foo/long ?l]\n" +
|
||||||
|
" [?e :foo/double ?d]\n" +
|
||||||
|
" [?e :foo/string ?s]\n" +
|
||||||
|
" [?e :foo/keyword ?k]\n" +
|
||||||
|
" [?e :foo/ref ?r]]";
|
||||||
|
|
||||||
|
final Expectation expectation1 = new Expectation();
|
||||||
|
final Date previousDate = new Date(1514804400000l);
|
||||||
|
final UUID previousUuid = UUID.fromString("4cb3f828-752d-497a-90c9-b1fd516d5644");
|
||||||
|
mentat.query(query).bindEntidReference("?e", bEntid).run(new TupleResultHandler() {
|
||||||
|
@Override
|
||||||
|
public void handleRow(TupleResult row) {
|
||||||
|
assertNotNull(row);
|
||||||
|
assertEquals(false, row.asBool(0));
|
||||||
|
assertEquals(previousDate, row.asDate(1));
|
||||||
|
assertEquals(previousUuid, row.asUUID(2));
|
||||||
|
assertEquals(50, row.asLong(3).longValue());
|
||||||
|
assertEquals(new Double(22.46), row.asDouble(4));
|
||||||
|
assertEquals("Silence is worse; all truths that are kept silent become poisonous.", row.asString(5));
|
||||||
|
assertEquals(":foo/string", row.asKeyword(6));
|
||||||
|
assertEquals(stringEntid, row.asEntid(7).longValue());
|
||||||
|
expectation1.fulfill();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
synchronized (expectation1) {
|
||||||
|
expectation1.wait(1000);
|
||||||
|
}
|
||||||
|
assertTrue(expectation1.isFulfilled);
|
||||||
|
|
||||||
|
InProgressBuilder builder = mentat.entityBuilder();
|
||||||
|
builder.retract(bEntid, ":foo/boolean", false);
|
||||||
|
builder.retract(bEntid, ":foo/instant", previousDate);
|
||||||
|
builder.retract(bEntid, ":foo/uuid", previousUuid);
|
||||||
|
builder.retract(bEntid, ":foo/long", 50);
|
||||||
|
builder.retract(bEntid, ":foo/double", 22.46);
|
||||||
|
builder.retract(bEntid, ":foo/string", "Silence is worse; all truths that are kept silent become poisonous.");
|
||||||
|
builder.retractKeyword(bEntid, ":foo/keyword", ":foo/string");
|
||||||
|
builder.retractRef(bEntid, ":foo/ref", stringEntid);
|
||||||
|
builder.commit();
|
||||||
|
|
||||||
|
final Expectation expectation2 = new Expectation();
|
||||||
|
mentat.query(query).bindEntidReference("?e", bEntid).run(new TupleResultHandler() {
|
||||||
|
@Override
|
||||||
|
public void handleRow(TupleResult row) {
|
||||||
|
assertNull(row);
|
||||||
|
expectation2.fulfill();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
synchronized (expectation2) {
|
||||||
|
expectation2.wait(1000);
|
||||||
|
}
|
||||||
|
assertTrue(expectation2.isFulfilled);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCaching() throws InterruptedException {
|
||||||
|
String query = "[:find ?district :where\n" +
|
||||||
|
" [?neighborhood :neighborhood/name \"Beacon Hill\"]\n" +
|
||||||
|
" [?neighborhood :neighborhood/district ?d]\n" +
|
||||||
|
" [?d :district/name ?district]]";
|
||||||
|
|
||||||
|
Mentat mentat = openAndInitializeCitiesStore();
|
||||||
|
|
||||||
|
final Expectation expectation1 = new Expectation();
|
||||||
|
final QueryTimer uncachedTimer = new QueryTimer();
|
||||||
|
uncachedTimer.start();
|
||||||
|
mentat.query(query).run(new RelResultHandler() {
|
||||||
|
@Override
|
||||||
|
public void handleRows(RelResult rows) {
|
||||||
|
uncachedTimer.end();
|
||||||
|
assertNotNull(rows);
|
||||||
|
expectation1.fulfill();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
synchronized (expectation1) {
|
||||||
|
expectation1.wait(1000);
|
||||||
|
}
|
||||||
|
assertTrue(expectation1.isFulfilled);
|
||||||
|
|
||||||
|
mentat.cache(":neighborhood/name", CacheDirection.REVERSE);
|
||||||
|
mentat.cache(":neighborhood/district", CacheDirection.FORWARD);
|
||||||
|
|
||||||
|
final Expectation expectation2 = new Expectation();
|
||||||
|
final QueryTimer cachedTimer = new QueryTimer();
|
||||||
|
cachedTimer.start();
|
||||||
|
mentat.query(query).run(new RelResultHandler() {
|
||||||
|
@Override
|
||||||
|
public void handleRows(RelResult rows) {
|
||||||
|
cachedTimer.end();
|
||||||
|
assertNotNull(rows);
|
||||||
|
expectation2.fulfill();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
synchronized (expectation2) {
|
||||||
|
expectation2.wait(1000);
|
||||||
|
}
|
||||||
|
assertTrue(expectation2.isFulfilled);
|
||||||
|
|
||||||
|
long timingDifference = uncachedTimer.duration() - cachedTimer.duration();
|
||||||
|
Log.d("testCaching", "Cached query is "+ timingDifference +" nanoseconds faster than the uncached query");
|
||||||
|
|
||||||
|
assert cachedTimer.duration() < uncachedTimer.duration();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||||
|
* Copyright 2018 Mozilla
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License. */
|
||||||
|
|
||||||
|
package com.mozilla.mentat;
|
||||||
|
|
||||||
|
public enum CacheDirection {
|
||||||
|
FORWARD, REVERSE, BOTH
|
||||||
|
}
|
|
@ -0,0 +1,357 @@
|
||||||
|
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||||
|
* Copyright 2018 Mozilla
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License. */
|
||||||
|
|
||||||
|
package com.mozilla.mentat;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.sun.jna.Memory;
|
||||||
|
import com.sun.jna.Pointer;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class wraps a raw pointer that points to a Rust `EntityBuilder<InProgressBuilder>` object.
|
||||||
|
*
|
||||||
|
* {@link EntityBuilder} provides a programmatic interface to performing assertions on a specific entity.
|
||||||
|
* It provides functions for adding and retracting values for attributes for an entity within
|
||||||
|
* an in progress transaction.
|
||||||
|
* <p/>
|
||||||
|
* The `transact` function will transact the assertions that have been added to the {@link EntityBuilder}
|
||||||
|
* and pass back the {@link TxReport} that was generated by this transact and the {@link InProgress} that was
|
||||||
|
* used to perform the transact. This enables you to perform further transacts on the same {@link InProgress}
|
||||||
|
* before committing.
|
||||||
|
* <p/>
|
||||||
|
* <pre>{@code
|
||||||
|
* long aEntid = txReport.getEntidForTempId("a");
|
||||||
|
* long bEntid = txReport.getEntidForTempId("b");
|
||||||
|
* EntityBuilder builder = mentat.getEntityBuilderForEntid(bEntid);
|
||||||
|
* builder.add(":foo/boolean", true);
|
||||||
|
* builder.add(":foo/instant", newDate);
|
||||||
|
* InProgress inProgress = builder.transact().getInProgress();
|
||||||
|
* inProgress.transact("[[:db/add \(aEntid) :foo/long 22]]");
|
||||||
|
* inProgress.commit();
|
||||||
|
* }</pre>
|
||||||
|
* <p/>
|
||||||
|
* The `commit` function will transact and commit the assertions that have been added to the {@link EntityBuilder}.
|
||||||
|
* It will consume the {@link InProgress} used to perform the transact. It returns the {@link TxReport} generated by
|
||||||
|
* the transact. After calling `commit`, a new transaction must be started by calling <pre>Mentat.beginTransaction()</pre>
|
||||||
|
* in order to perform further actions.
|
||||||
|
* <p/>
|
||||||
|
* <pre>{@code
|
||||||
|
* long aEntid = txReport.getEntidForTempId("a");
|
||||||
|
* EntityBuilder builder = mentat.getEntityBuilderForEntid(bEntid);
|
||||||
|
* builder.add(":foo/boolean", true);
|
||||||
|
* builder.add(":foo/instant", newDate);
|
||||||
|
* builder.commit();
|
||||||
|
* }</pre>
|
||||||
|
*/
|
||||||
|
public class EntityBuilder extends RustObject {
|
||||||
|
|
||||||
|
public EntityBuilder(Pointer pointer) {
|
||||||
|
this.rawPointer = pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts the value of attribute `keyword` to be the provided `value`.
|
||||||
|
* @param keyword The name of the attribute in the format `:namespace/name`.
|
||||||
|
* @param value The value to be asserted
|
||||||
|
*/
|
||||||
|
public void add(String keyword, long value) {
|
||||||
|
this.validate();
|
||||||
|
RustResult result = JNA.INSTANCE.entity_builder_add_long(this.rawPointer, keyword, value);
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("EntityBuilder", result.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts the value of attribute `keyword` to be the provided `value`.
|
||||||
|
* // TODO throw exception if error occurs
|
||||||
|
* @param keyword The name of the attribute in the format `:namespace/name`.
|
||||||
|
* @param value The value to be asserted
|
||||||
|
*/
|
||||||
|
public void addRef(String keyword, long value) {
|
||||||
|
this.validate();
|
||||||
|
RustResult result = JNA.INSTANCE.entity_builder_add_ref(this.rawPointer, keyword, value);
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("EntityBuilder", result.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts the value of attribute `keyword` to be the provided `value`.
|
||||||
|
* // TODO throw exception if error occurs
|
||||||
|
* @param keyword The name of the attribute in the format `:namespace/name`.
|
||||||
|
* @param value The value to be asserted
|
||||||
|
*/
|
||||||
|
public void addKeyword(String keyword, String value) {
|
||||||
|
this.validate();
|
||||||
|
RustResult result = JNA.INSTANCE.entity_builder_add_keyword(this.rawPointer, keyword, value);
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("EntityBuilder", result.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts the value of attribute `keyword` to be the provided `value`.
|
||||||
|
* // TODO throw exception if error occurs
|
||||||
|
* @param keyword The name of the attribute in the format `:namespace/name`.
|
||||||
|
* @param value The value to be asserted
|
||||||
|
*/
|
||||||
|
public void add(String keyword, boolean value) {
|
||||||
|
this.validate();
|
||||||
|
RustResult result = JNA.INSTANCE.entity_builder_add_boolean(this.rawPointer, keyword, value ? 1 : 0);
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("EntityBuilder", result.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts the value of attribute `keyword` to be the provided `value`.
|
||||||
|
* // TODO throw exception if error occurs
|
||||||
|
* @param keyword The name of the attribute in the format `:namespace/name`.
|
||||||
|
* @param value The value to be asserted
|
||||||
|
*/
|
||||||
|
public void add(String keyword, double value) {
|
||||||
|
this.validate();
|
||||||
|
RustResult result = JNA.INSTANCE.entity_builder_add_double(this.rawPointer, keyword, value);
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("EntityBuilder", result.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts the value of attribute `keyword` to be the provided `value`.
|
||||||
|
* // TODO throw exception if error occurs
|
||||||
|
* @param keyword The name of the attribute in the format `:namespace/name`.
|
||||||
|
* @param value The value to be asserted
|
||||||
|
*/
|
||||||
|
public void add(String keyword, Date value) {
|
||||||
|
this.validate();
|
||||||
|
RustResult result = JNA.INSTANCE.entity_builder_add_timestamp(this.rawPointer, keyword, value.getTime() * 1_000);
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("EntityBuilder", result.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts the value of attribute `keyword` to be the provided `value`.
|
||||||
|
* // TODO throw exception if error occurs
|
||||||
|
* @param keyword The name of the attribute in the format `:namespace/name`.
|
||||||
|
* @param value The value to be asserted
|
||||||
|
*/
|
||||||
|
public void add(String keyword, String value) {
|
||||||
|
this.validate();
|
||||||
|
RustResult result = JNA.INSTANCE.entity_builder_add_string(this.rawPointer, keyword, value);
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("EntityBuilder", result.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts the value of attribute `keyword` to be the provided `value`.
|
||||||
|
* // TODO throw exception if error occurs
|
||||||
|
* @param keyword The name of the attribute in the format `:namespace/name`.
|
||||||
|
* @param value The value to be asserted
|
||||||
|
*/
|
||||||
|
public void add(String keyword, UUID value) {
|
||||||
|
this.validate();
|
||||||
|
|
||||||
|
RustResult result = JNA.INSTANCE.entity_builder_add_uuid(this.rawPointer, keyword, getPointerForUUID(value));
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("EntityBuilder", result.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retracts the value of attribute `keyword` from the provided `value`.
|
||||||
|
*
|
||||||
|
* TODO throw exception if error occurs
|
||||||
|
*
|
||||||
|
* @param keyword The name of the attribute in the format `:namespace/name`.
|
||||||
|
* @param value The value to be retracted
|
||||||
|
*/
|
||||||
|
public void retract(String keyword, long value) {
|
||||||
|
this.validate();
|
||||||
|
RustResult result = JNA.INSTANCE.entity_builder_retract_long(this.rawPointer, keyword, value);
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("EntityBuilder", result.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retracts the value of attribute `keyword` from the provided `value`.
|
||||||
|
*
|
||||||
|
* TODO throw exception if error occurs
|
||||||
|
*
|
||||||
|
* @param keyword The name of the attribute in the format `:namespace/name`.
|
||||||
|
* @param value The value to be retracted
|
||||||
|
*/
|
||||||
|
public void retractRef(String keyword, long value) {
|
||||||
|
this.validate();
|
||||||
|
RustResult result = JNA.INSTANCE.entity_builder_retract_ref(this.rawPointer, keyword, value);
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("EntityBuilder", result.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retracts the value of attribute `keyword` from the provided `value`.
|
||||||
|
*
|
||||||
|
* TODO throw exception if error occurs
|
||||||
|
*
|
||||||
|
* @param keyword The name of the attribute in the format `:namespace/name`.
|
||||||
|
* @param value The value to be retracted
|
||||||
|
*/
|
||||||
|
public void retractKeyword(String keyword, String value) {
|
||||||
|
this.validate();
|
||||||
|
RustResult result = JNA.INSTANCE.entity_builder_retract_keyword(this.rawPointer, keyword, value);
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("EntityBuilder", result.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retracts the value of attribute `keyword` from the provided `value`.
|
||||||
|
*
|
||||||
|
* TODO throw exception if error occurs
|
||||||
|
*
|
||||||
|
* @param keyword The name of the attribute in the format `:namespace/name`.
|
||||||
|
* @param value The value to be retracted
|
||||||
|
*/
|
||||||
|
public void retract(String keyword, boolean value) {
|
||||||
|
this.validate();
|
||||||
|
RustResult result = JNA.INSTANCE.entity_builder_retract_boolean(this.rawPointer, keyword, value ? 1 : 0);
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("EntityBuilder", result.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retracts the value of attribute `keyword` from the provided `value`.
|
||||||
|
*
|
||||||
|
* TODO throw exception if error occurs
|
||||||
|
*
|
||||||
|
* @param keyword The name of the attribute in the format `:namespace/name`.
|
||||||
|
* @param value The value to be retracted
|
||||||
|
*/
|
||||||
|
public void retract(String keyword, double value) {
|
||||||
|
this.validate();
|
||||||
|
RustResult result = JNA.INSTANCE.entity_builder_retract_double(this.rawPointer, keyword, value);
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("EntityBuilder", result.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retracts the value of attribute `keyword` from the provided `value`.
|
||||||
|
*
|
||||||
|
* TODO throw exception if error occurs
|
||||||
|
*
|
||||||
|
* @param keyword The name of the attribute in the format `:namespace/name`.
|
||||||
|
* @param value The value to be retracted
|
||||||
|
*/
|
||||||
|
public void retract(String keyword, Date value) {
|
||||||
|
this.validate();
|
||||||
|
RustResult result = JNA.INSTANCE.entity_builder_retract_timestamp(this.rawPointer, keyword, value.getTime() * 1_000);
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("EntityBuilder", result.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retracts the value of attribute `keyword` from the provided `value`.
|
||||||
|
*
|
||||||
|
* TODO throw exception if error occurs
|
||||||
|
*
|
||||||
|
* @param keyword The name of the attribute in the format `:namespace/name`.
|
||||||
|
* @param value The value to be retracted
|
||||||
|
*/
|
||||||
|
public void retract(String keyword, String value) {
|
||||||
|
this.validate();
|
||||||
|
RustResult result = JNA.INSTANCE.entity_builder_retract_string(this.rawPointer, keyword, value);
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("EntityBuilder", result.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retracts the value of attribute `keyword` from the provided `value`.
|
||||||
|
*
|
||||||
|
* TODO throw exception if error occurs
|
||||||
|
*
|
||||||
|
* @param keyword The name of the attribute in the format `:namespace/name`.
|
||||||
|
* @param value The value to be retracted
|
||||||
|
*/
|
||||||
|
public void retract(String keyword, UUID value) {
|
||||||
|
this.validate();
|
||||||
|
RustResult result = JNA.INSTANCE.entity_builder_retract_uuid(this.rawPointer, keyword, this.getPointerForUUID(value));
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("EntityBuilder", result.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transacts the added assertions. This consumes the pointer associated with this {@link EntityBuilder}
|
||||||
|
* such that no further assertions can be added after the `transact` has completed. To perform
|
||||||
|
* further assertions, use the {@link InProgress} inside the {@link InProgressTransactionResult}
|
||||||
|
* returned from this function.
|
||||||
|
* <p/>
|
||||||
|
* This does not commit the transaction. In order to do so, `commit` can be called on the
|
||||||
|
* {@link InProgress} inside the {@link InProgressTransactionResult} returned from this function.
|
||||||
|
*
|
||||||
|
* TODO throw exception if error occurs
|
||||||
|
*
|
||||||
|
* @return A {@link InProgressTransactionResult} containing the current {@link InProgress} and
|
||||||
|
* the {@link TxReport} generated by the transact.
|
||||||
|
*/
|
||||||
|
public InProgressTransactionResult transact() {
|
||||||
|
this.validate();
|
||||||
|
InProgressTransactionResult result = JNA.INSTANCE.entity_builder_transact(this.rawPointer);
|
||||||
|
this.rawPointer = null;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transacts the added assertions and commits. This consumes the pointer associated with this
|
||||||
|
* {@link EntityBuilder} and the associated {@link InProgress} such that no further assertions
|
||||||
|
* can be added after the `commit` has completed.
|
||||||
|
* <p/>
|
||||||
|
* To perform further assertions, a new `{@link InProgress} or {@link EntityBuilder} should be
|
||||||
|
* created.
|
||||||
|
*
|
||||||
|
* TODO throw exception if error occurs
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public TxReport commit() {
|
||||||
|
this.validate();
|
||||||
|
RustResult result = JNA.INSTANCE.entity_builder_commit(this.rawPointer);
|
||||||
|
this.rawPointer = null;
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("EntityBuilder", result.err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TxReport(result.ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
if (this.rawPointer != null) {
|
||||||
|
JNA.INSTANCE.entity_builder_destroy(this.rawPointer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,195 @@
|
||||||
|
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||||
|
* Copyright 2018 Mozilla
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License. */
|
||||||
|
|
||||||
|
package com.mozilla.mentat;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.sun.jna.Pointer;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class wraps a raw pointer that points to a Rust {@link InProgress} object.
|
||||||
|
* </p>
|
||||||
|
* {@link InProgress} allows for multiple transacts to be performed in a single transaction.
|
||||||
|
* Each transact performed results in a {@link TxReport} that can be used to gather information
|
||||||
|
* to be used in subsequent transacts.
|
||||||
|
* </p>
|
||||||
|
* Committing an {@link InProgress} commits all the transacts that have been performed using
|
||||||
|
* that {@link InProgress}.
|
||||||
|
* </p>
|
||||||
|
* Rolling back and {@link InProgress} rolls back all the transacts that have been performed
|
||||||
|
* using that {@link InProgress}.
|
||||||
|
* </p>
|
||||||
|
* <pre>{@code
|
||||||
|
* do {
|
||||||
|
* let inProgress = try mentat.beginTransaction()
|
||||||
|
* let txReport = try inProgress.transact(transaction: "[[:db/add "a" :foo/long 22]]")
|
||||||
|
* let aEntid = txReport.entid(forTempId: "a")
|
||||||
|
* let report = try inProgress.transact(transaction: "[[:db/add "b" :foo/ref \(aEntid)] [:db/add "b" :foo/boolean true]]")
|
||||||
|
* try inProgress.commit()
|
||||||
|
* } catch {
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
* }</pre>
|
||||||
|
* </p>
|
||||||
|
* {@link InProgress} also provides a number of functions to generating an builder to assert datoms
|
||||||
|
* programatically. The two types of builder are {@link InProgressBuilder} and {@link EntityBuilder}.
|
||||||
|
* </p>
|
||||||
|
* {@link InProgressBuilder} takes the current {@link InProgress} and provides a programmatic
|
||||||
|
* interface to add and retract values from entities for which there exists an `Entid`. The provided
|
||||||
|
* {@link InProgress} is used to perform the transacts.
|
||||||
|
* </p>
|
||||||
|
* <pre>{@code
|
||||||
|
* long aEntid = txReport.getEntidForTempId("a");
|
||||||
|
* long bEntid = txReport.getEntidForTempId("b");
|
||||||
|
* InProgress inProgress = mentat.beginTransaction();
|
||||||
|
* InProgressBuilder builder = inProgress.builder();
|
||||||
|
* builder.add(bEntid, ":foo/boolean", true);
|
||||||
|
* builder.add(aEntid, ":foo/instant", newDate);
|
||||||
|
* inProgress = builder.transact().getInProgress();
|
||||||
|
* inProgress.transact("[[:db/add \(aEntid) :foo/long 22]]");
|
||||||
|
* inProgress.commit();
|
||||||
|
* }
|
||||||
|
* }</pre>
|
||||||
|
* </p>
|
||||||
|
* {@link EntityBuilder} takes the current {@link InProgress} and either an `Entid` or a `tempid` to
|
||||||
|
* provide a programmatic interface to add and retract values from a specific entity. The provided
|
||||||
|
* {@link InProgress} is used to perform the transacts.
|
||||||
|
* </p>
|
||||||
|
* <pre>{@code
|
||||||
|
* long aEntid = txReport.getEntidForTempId("a");
|
||||||
|
* long bEntid = txReport.getEntidForTempId("b");
|
||||||
|
* InProgress inProgress = mentat.beginTransaction();
|
||||||
|
* EntityBuilder builder = inProgress.builderForEntid(bEntid);
|
||||||
|
* builder.add(":foo/boolean", true);
|
||||||
|
* builder.add(":foo/instant", newDate);
|
||||||
|
* inProgress = builder.transact().getInProgress();
|
||||||
|
* inProgress.transact("[[:db/add \(aEntid) :foo/long 22]]");
|
||||||
|
* inProgress.commit();
|
||||||
|
* }</pre>
|
||||||
|
*/
|
||||||
|
public class InProgress extends RustObject {
|
||||||
|
|
||||||
|
public InProgress(Pointer pointer) {
|
||||||
|
this.rawPointer = pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an {@link InProgressBuilder} using this {@link InProgress} .
|
||||||
|
*
|
||||||
|
* @return an {@link InProgressBuilder} for this {@link InProgress}
|
||||||
|
*/
|
||||||
|
public InProgressBuilder builder() {
|
||||||
|
this.validate();
|
||||||
|
InProgressBuilder builder = new InProgressBuilder(JNA.INSTANCE.in_progress_builder(this.rawPointer));
|
||||||
|
this.rawPointer = null;
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an `EntityBuilder` using this `InProgress` for the entity with `entid`.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param entid The `Entid` for this entity.
|
||||||
|
* @return an `EntityBuilder` for this `InProgress`
|
||||||
|
*/
|
||||||
|
public EntityBuilder builderForEntid(long entid){
|
||||||
|
this.validate();
|
||||||
|
EntityBuilder builder = new EntityBuilder(JNA.INSTANCE.in_progress_entity_builder_from_entid(this.rawPointer, entid));
|
||||||
|
this.rawPointer = null;
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an `EntityBuilder` using this `InProgress` for a new entity with `tempid`.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param tempid The temporary identifier for this entity.
|
||||||
|
* @return an `EntityBuilder` for this `InProgress`
|
||||||
|
*/
|
||||||
|
public EntityBuilder builderForTempid(String tempid){
|
||||||
|
this.validate();
|
||||||
|
EntityBuilder builder = new EntityBuilder(JNA.INSTANCE.in_progress_entity_builder_from_temp_id(this.rawPointer, tempid));
|
||||||
|
this.rawPointer = null;
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Transacts the `transaction`
|
||||||
|
|
||||||
|
This does not commit the transaction. In order to do so, `commit` can be called.
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if the transaction failed.
|
||||||
|
- Throws: `ResultError.empty` if no `TxReport` is returned from the transact.
|
||||||
|
|
||||||
|
- Returns: The `TxReport` generated by the transact.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Transacts the `transaction`
|
||||||
|
*
|
||||||
|
* This does not commit the transaction. In order to do so, `commit` can be called.
|
||||||
|
*
|
||||||
|
* TODO throw Exception on result failure.
|
||||||
|
*
|
||||||
|
* @param transaction The EDN string to be transacted.
|
||||||
|
* @return The `TxReport` generated by the transact.
|
||||||
|
*/
|
||||||
|
public TxReport transact(String transaction) {
|
||||||
|
this.validate();
|
||||||
|
RustResult result = JNA.INSTANCE.in_progress_transact(this.rawPointer, transaction);
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("InProgress", result.err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new TxReport(result.ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Commits all the transacts that have been performed on this `InProgress`, either directly
|
||||||
|
* or through a Builder. This consumes the pointer associated with this {@link InProgress} such
|
||||||
|
* that no further assertions can be performed after the `commit` has completed.
|
||||||
|
*
|
||||||
|
* TODO throw exception if error occurs
|
||||||
|
*/
|
||||||
|
public void commit() {
|
||||||
|
this.validate();
|
||||||
|
RustResult result = JNA.INSTANCE.in_progress_commit(this.rawPointer);
|
||||||
|
this.rawPointer = null;
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("InProgressBuilder", result.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rolls back all the transacts that have been performed on this `InProgress`, either directly
|
||||||
|
* or through a Builder.
|
||||||
|
*
|
||||||
|
* TODO throw exception if error occurs
|
||||||
|
*/
|
||||||
|
public void rollback() {
|
||||||
|
this.validate();
|
||||||
|
RustResult result = JNA.INSTANCE.in_progress_rollback(this.rawPointer);
|
||||||
|
this.rawPointer = null;
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("InProgressBuilder", result.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
if (this.rawPointer != null) {
|
||||||
|
JNA.INSTANCE.in_progress_destroy(this.rawPointer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,382 @@
|
||||||
|
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||||
|
* Copyright 2018 Mozilla
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License. */
|
||||||
|
|
||||||
|
package com.mozilla.mentat;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.sun.jna.Pointer;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class wraps a raw pointer that points to a Rust `InProgressBuilder` object.
|
||||||
|
*
|
||||||
|
* {@link InProgressBuilder} provides a programmatic interface to performing assertions for entities.
|
||||||
|
* It provides functions for adding and retracting values for attributes for an entity within
|
||||||
|
* an in progress transaction.
|
||||||
|
* <p/>
|
||||||
|
* The `transact` function will transact the assertions that have been added to the {@link InProgressBuilder}
|
||||||
|
* and pass back the {@link TxReport} that was generated by this transact and the {@link InProgress} that was
|
||||||
|
* used to perform the transact. This enables you to perform further transacts on the same {@link InProgress}
|
||||||
|
* before committing.
|
||||||
|
* <p/>
|
||||||
|
* <pre>{@code
|
||||||
|
* long aEntid = txReport.getEntidForTempId("a");
|
||||||
|
* long bEntid = txReport.getEntidForTempId("b");
|
||||||
|
* InProgressBuilder builder = mentat.getEntityBuilder();
|
||||||
|
* builder.add(aEntid, ":foo/boolean", true);
|
||||||
|
* builder.add(bEntid, ":foo/instant", newDate);
|
||||||
|
* InProgress inProgress = builder.transact().getInProgress();
|
||||||
|
* inProgress.transact("[[:db/add \(aEntid) :foo/long 22]]");
|
||||||
|
* inProgress.commit();
|
||||||
|
* }</pre>
|
||||||
|
* <p/>
|
||||||
|
* The `commit` function will transact and commit the assertions that have been added to the
|
||||||
|
* {@link InProgressBuilder}.
|
||||||
|
* It will consume the {@link InProgress} used to perform the transact. It returns the {@link TxReport}
|
||||||
|
* generated by the transact. After calling `commit`, a new transaction must be started by calling
|
||||||
|
* <pre>Mentat.beginTransaction()</pre> in order to perform further actions.
|
||||||
|
* <p/>
|
||||||
|
* <pre>{@code
|
||||||
|
* long aEntid = txReport.getEntidForTempId("a");
|
||||||
|
* long bEntid = txReport.getEntidForTempId("b");
|
||||||
|
* InProgressBuilder builder = mentat.getEntityBuilder();
|
||||||
|
* builder.add(aEntid, ":foo/boolean", true);
|
||||||
|
* builder.add(bEntid, ":foo/instant", newDate);
|
||||||
|
* builder.commit();
|
||||||
|
* }</pre>
|
||||||
|
*/
|
||||||
|
public class InProgressBuilder extends RustObject {
|
||||||
|
|
||||||
|
public InProgressBuilder(Pointer pointer) {
|
||||||
|
this.rawPointer = pointer;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Asserts the value of attribute `keyword` to be the provided `value`.
|
||||||
|
*
|
||||||
|
* // TODO throw exception if error occurs
|
||||||
|
*
|
||||||
|
* @param entid The `Entid` of the entity to be touched.
|
||||||
|
* @param keyword The name of the attribute in the format `:namespace/name`.
|
||||||
|
* @param value The value to be asserted
|
||||||
|
*/
|
||||||
|
public void add(long entid, String keyword, long value) {
|
||||||
|
this.validate();
|
||||||
|
RustResult result = JNA.INSTANCE.in_progress_builder_add_long(this.rawPointer, entid, keyword, value);
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("InProgressBuilder", result.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts the value of attribute `keyword` to be the provided `value`.
|
||||||
|
* // TODO throw exception if error occurs
|
||||||
|
*
|
||||||
|
* @param entid The `Entid` of the entity to be touched.
|
||||||
|
* @param keyword The name of the attribute in the format `:namespace/name`.
|
||||||
|
* @param value The value to be asserted
|
||||||
|
*/
|
||||||
|
public void addRef(long entid, String keyword, long value) {
|
||||||
|
this.validate();
|
||||||
|
RustResult result = JNA.INSTANCE.in_progress_builder_add_ref(this.rawPointer, entid, keyword, value);
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("InProgressBuilder", result.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts the value of attribute `keyword` to be the provided `value`.
|
||||||
|
* // TODO throw exception if error occurs
|
||||||
|
*
|
||||||
|
* @param entid The `Entid` of the entity to be touched.
|
||||||
|
* @param keyword The name of the attribute in the format `:namespace/name`.
|
||||||
|
* @param value The value to be asserted
|
||||||
|
*/
|
||||||
|
public void addKeyword(long entid, String keyword, String value) {
|
||||||
|
this.validate();
|
||||||
|
RustResult result = JNA.INSTANCE.in_progress_builder_add_keyword(this.rawPointer, entid, keyword, value);
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("InProgressBuilder", result.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts the value of attribute `keyword` to be the provided `value`.
|
||||||
|
* // TODO throw exception if error occurs
|
||||||
|
*
|
||||||
|
* @param entid The `Entid` of the entity to be touched.
|
||||||
|
* @param keyword The name of the attribute in the format `:namespace/name`.
|
||||||
|
* @param value The value to be asserted
|
||||||
|
*/
|
||||||
|
public void add(long entid, String keyword, boolean value) {
|
||||||
|
this.validate();
|
||||||
|
RustResult result = JNA.INSTANCE.in_progress_builder_add_boolean(this.rawPointer, entid, keyword, value ? 1 : 0);
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("InProgressBuilder", result.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts the value of attribute `keyword` to be the provided `value`.
|
||||||
|
* // TODO throw exception if error occurs
|
||||||
|
*
|
||||||
|
* @param entid The `Entid` of the entity to be touched.
|
||||||
|
* @param keyword The name of the attribute in the format `:namespace/name`.
|
||||||
|
* @param value The value to be asserted
|
||||||
|
*/
|
||||||
|
public void add(long entid, String keyword, double value) {
|
||||||
|
this.validate();
|
||||||
|
RustResult result = JNA.INSTANCE.in_progress_builder_add_double(this.rawPointer, entid, keyword, value);
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("InProgressBuilder", result.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts the value of attribute `keyword` to be the provided `value`.
|
||||||
|
* // TODO throw exception if error occurs
|
||||||
|
*
|
||||||
|
* @param entid The `Entid` of the entity to be touched.
|
||||||
|
* @param keyword The name of the attribute in the format `:namespace/name`.
|
||||||
|
* @param value The value to be asserted
|
||||||
|
*/
|
||||||
|
public void add(long entid, String keyword, Date value) {
|
||||||
|
this.validate();
|
||||||
|
RustResult result = JNA.INSTANCE.in_progress_builder_add_timestamp(this.rawPointer, entid, keyword, value.getTime() * 1_000);
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("InProgressBuilder", result.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts the value of attribute `keyword` to be the provided `value`.
|
||||||
|
* // TODO throw exception if error occurs
|
||||||
|
*
|
||||||
|
* @param entid The `Entid` of the entity to be touched.
|
||||||
|
* @param keyword The name of the attribute in the format `:namespace/name`.
|
||||||
|
* @param value The value to be asserted
|
||||||
|
*/
|
||||||
|
public void add(long entid, String keyword, String value) {
|
||||||
|
this.validate();
|
||||||
|
RustResult result = JNA.INSTANCE.in_progress_builder_add_string(this.rawPointer, entid, keyword, value);
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("InProgressBuilder", result.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts the value of attribute `keyword` to be the provided `value`.
|
||||||
|
* // TODO throw exception if error occurs
|
||||||
|
*
|
||||||
|
* @param entid The `Entid` of the entity to be touched.
|
||||||
|
* @param keyword The name of the attribute in the format `:namespace/name`.
|
||||||
|
* @param value The value to be asserted
|
||||||
|
*/
|
||||||
|
public void add(long entid, String keyword, UUID value) {
|
||||||
|
this.validate();
|
||||||
|
|
||||||
|
RustResult result = JNA.INSTANCE.in_progress_builder_add_uuid(this.rawPointer, entid, keyword, getPointerForUUID(value));
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("InProgressBuilder", result.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retracts the value of attribute `keyword` from the provided `value`.
|
||||||
|
*
|
||||||
|
* TODO throw exception if error occurs
|
||||||
|
*
|
||||||
|
* @param entid The `Entid` of the entity to be touched.
|
||||||
|
* @param keyword The name of the attribute in the format `:namespace/name`.
|
||||||
|
* @param value The value to be retracted
|
||||||
|
*/
|
||||||
|
public void retract(long entid, String keyword, long value) {
|
||||||
|
this.validate();
|
||||||
|
RustResult result = JNA.INSTANCE.in_progress_builder_retract_long(this.rawPointer, entid, keyword, value);
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("InProgressBuilder", result.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retracts the value of attribute `keyword` from the provided `value`.
|
||||||
|
*
|
||||||
|
* TODO throw exception if error occurs
|
||||||
|
*
|
||||||
|
* @param entid The `Entid` of the entity to be touched.
|
||||||
|
* @param keyword The name of the attribute in the format `:namespace/name`.
|
||||||
|
* @param value The value to be retracted
|
||||||
|
*/
|
||||||
|
public void retractRef(long entid, String keyword, long value) {
|
||||||
|
this.validate();
|
||||||
|
RustResult result = JNA.INSTANCE.in_progress_builder_retract_ref(this.rawPointer, entid, keyword, value);
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("InProgressBuilder", result.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retracts the value of attribute `keyword` from the provided `value`.
|
||||||
|
*
|
||||||
|
* TODO throw exception if error occurs
|
||||||
|
*
|
||||||
|
* @param entid The `Entid` of the entity to be touched.
|
||||||
|
* @param keyword The name of the attribute in the format `:namespace/name`.
|
||||||
|
* @param value The value to be retracted
|
||||||
|
*/
|
||||||
|
public void retractKeyword(long entid, String keyword, String value) {
|
||||||
|
this.validate();
|
||||||
|
RustResult result = JNA.INSTANCE.in_progress_builder_retract_keyword(this.rawPointer, entid, keyword, value);
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("InProgressBuilder", result.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retracts the value of attribute `keyword` from the provided `value`.
|
||||||
|
*
|
||||||
|
* TODO throw exception if error occurs
|
||||||
|
*
|
||||||
|
* @param entid The `Entid` of the entity to be touched.
|
||||||
|
* @param keyword The name of the attribute in the format `:namespace/name`.
|
||||||
|
* @param value The value to be retracted
|
||||||
|
*/
|
||||||
|
public void retract(long entid, String keyword, boolean value) {
|
||||||
|
this.validate();
|
||||||
|
RustResult result = JNA.INSTANCE.in_progress_builder_retract_boolean(this.rawPointer, entid, keyword, value ? 1 : 0);
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("InProgressBuilder", result.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retracts the value of attribute `keyword` from the provided `value`.
|
||||||
|
*
|
||||||
|
* TODO throw exception if error occurs
|
||||||
|
*
|
||||||
|
* @param entid The `Entid` of the entity to be touched.
|
||||||
|
* @param keyword The name of the attribute in the format `:namespace/name`.
|
||||||
|
* @param value The value to be retracted
|
||||||
|
*/
|
||||||
|
public void retract(long entid, String keyword, double value) {
|
||||||
|
this.validate();
|
||||||
|
RustResult result = JNA.INSTANCE.in_progress_builder_retract_double(this.rawPointer, entid, keyword, value);
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("InProgressBuilder", result.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retracts the value of attribute `keyword` from the provided `value`.
|
||||||
|
*
|
||||||
|
* TODO throw exception if error occurs
|
||||||
|
*
|
||||||
|
* @param entid The `Entid` of the entity to be touched.
|
||||||
|
* @param keyword The name of the attribute in the format `:namespace/name`.
|
||||||
|
* @param value The value to be retracted
|
||||||
|
*/
|
||||||
|
public void retract(long entid, String keyword, Date value) {
|
||||||
|
this.validate();
|
||||||
|
RustResult result = JNA.INSTANCE.in_progress_builder_retract_timestamp(this.rawPointer, entid, keyword, value.getTime() * 1_000);
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("InProgressBuilder", result.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retracts the value of attribute `keyword` from the provided `value`.
|
||||||
|
*
|
||||||
|
* TODO throw exception if error occurs
|
||||||
|
*
|
||||||
|
* @param entid The `Entid` of the entity to be touched.
|
||||||
|
* @param keyword The name of the attribute in the format `:namespace/name`.
|
||||||
|
* @param value The value to be retracted
|
||||||
|
*/
|
||||||
|
public void retract(long entid, String keyword, String value) {
|
||||||
|
this.validate();
|
||||||
|
RustResult result = JNA.INSTANCE.in_progress_builder_retract_string(this.rawPointer, entid, keyword, value);
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("InProgressBuilder", result.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retracts the value of attribute `keyword` from the provided `value`.
|
||||||
|
*
|
||||||
|
* TODO throw exception if error occurs
|
||||||
|
*
|
||||||
|
* @param entid The `Entid` of the entity to be touched.
|
||||||
|
* @param keyword The name of the attribute in the format `:namespace/name`.
|
||||||
|
* @param value The value to be retracted
|
||||||
|
*/
|
||||||
|
public void retract(long entid, String keyword, UUID value) {
|
||||||
|
this.validate();
|
||||||
|
RustResult result = JNA.INSTANCE.in_progress_builder_retract_uuid(this.rawPointer, entid, keyword, this.getPointerForUUID(value));
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("InProgressBuilder", result.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transacts the added assertions. This consumes the pointer associated with this {@link InProgressBuilder}
|
||||||
|
* such that no further assertions can be added after the `transact` has completed. To perform
|
||||||
|
* further assertions, use the {@link InProgress} inside the {@link InProgressTransactionResult}
|
||||||
|
* returned from this function.
|
||||||
|
* <p/>
|
||||||
|
* This does not commit the transaction. In order to do so, `commit` can be called on the
|
||||||
|
* {@link InProgress} inside the {@link InProgressTransactionResult} returned from this function.
|
||||||
|
*
|
||||||
|
* TODO throw exception if error occurs
|
||||||
|
*
|
||||||
|
* @return A {@link InProgressTransactionResult} containing the current {@link InProgress} and
|
||||||
|
* the {@link TxReport} generated by the transact.
|
||||||
|
*/
|
||||||
|
public InProgressTransactionResult transact() {
|
||||||
|
this.validate();
|
||||||
|
InProgressTransactionResult result = JNA.INSTANCE.in_progress_builder_transact(this.rawPointer);
|
||||||
|
this.rawPointer = null;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transacts the added assertions and commits. This consumes the pointer associated with this
|
||||||
|
* {@link InProgressBuilder} and the associated {@link InProgress} such that no further assertions
|
||||||
|
* can be added after the `commit` has completed.
|
||||||
|
* <p/>
|
||||||
|
* To perform further assertions, a new `{@link InProgress} or {@link InProgressBuilder} should be
|
||||||
|
* created.
|
||||||
|
*
|
||||||
|
* TODO throw exception if error occurs
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public TxReport commit() {
|
||||||
|
this.validate();
|
||||||
|
RustResult result = JNA.INSTANCE.in_progress_builder_commit(this.rawPointer);
|
||||||
|
this.rawPointer = null;
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("InProgressBuilder", result.err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TxReport(result.ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
if (this.rawPointer != null) {
|
||||||
|
JNA.INSTANCE.in_progress_builder_destroy(this.rawPointer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||||
|
* Copyright 2018 Mozilla
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License. */
|
||||||
|
|
||||||
|
package com.mozilla.mentat;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.sun.jna.Pointer;
|
||||||
|
import com.sun.jna.Structure;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class InProgressTransactionResult extends Structure implements Closeable {
|
||||||
|
public static class ByReference extends InProgressTransactionResult implements Structure.ByReference {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ByValue extends InProgressTransactionResult implements Structure.ByValue {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Pointer inProgress;
|
||||||
|
public RustResult.ByReference result;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<String> getFieldOrder() {
|
||||||
|
return Arrays.asList("inProgress", "result");
|
||||||
|
}
|
||||||
|
|
||||||
|
public InProgress getInProgress() {
|
||||||
|
return new InProgress(this.inProgress);
|
||||||
|
}
|
||||||
|
public TxReport getReport() {
|
||||||
|
if (this.result.isFailure()) {
|
||||||
|
Log.e("InProgressTransactionResult", this.result.err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TxReport(this.result.ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
if (this.getPointer() != null) {
|
||||||
|
JNA.INSTANCE.destroy(this.getPointer());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -37,12 +37,72 @@ public interface JNA extends Library {
|
||||||
void typed_value_result_set_destroy(Pointer obj);
|
void typed_value_result_set_destroy(Pointer obj);
|
||||||
void typed_value_result_set_iter_destroy(Pointer obj);
|
void typed_value_result_set_iter_destroy(Pointer obj);
|
||||||
void tx_report_destroy(Pointer obj);
|
void tx_report_destroy(Pointer obj);
|
||||||
|
void in_progress_destroy(Pointer obj);
|
||||||
|
void in_progress_builder_destroy(Pointer obj);
|
||||||
|
void entity_builder_destroy(Pointer obj);
|
||||||
|
|
||||||
|
// caching
|
||||||
|
RustResult store_cache_attribute_forward(Pointer store, String attribute);
|
||||||
|
RustResult store_cache_attribute_reverse(Pointer store, String attribute);
|
||||||
|
RustResult store_cache_attribute_bi_directional(Pointer store, String attribute);
|
||||||
|
|
||||||
// transact
|
// transact
|
||||||
RustResult store_transact(Pointer store, String transaction);
|
RustResult store_transact(Pointer store, String transaction);
|
||||||
Pointer tx_report_entity_for_temp_id(Pointer report, String tempid);
|
Pointer tx_report_entity_for_temp_id(Pointer report, String tempid);
|
||||||
long tx_report_get_entid(Pointer report);
|
long tx_report_get_entid(Pointer report);
|
||||||
long tx_report_get_tx_instant(Pointer report);
|
long tx_report_get_tx_instant(Pointer report);
|
||||||
|
RustResult store_begin_transaction(Pointer store);
|
||||||
|
|
||||||
|
// in progress
|
||||||
|
RustResult in_progress_transact(Pointer in_progress, String transaction);
|
||||||
|
RustResult in_progress_commit(Pointer in_progress);
|
||||||
|
RustResult in_progress_rollback(Pointer in_progress);
|
||||||
|
Pointer in_progress_builder(Pointer in_progress);
|
||||||
|
Pointer in_progress_entity_builder_from_temp_id(Pointer in_progress, String temp_id);
|
||||||
|
Pointer in_progress_entity_builder_from_entid(Pointer in_progress, long entid);
|
||||||
|
|
||||||
|
// in_progress entity building
|
||||||
|
RustResult store_in_progress_builder(Pointer store);
|
||||||
|
RustResult in_progress_builder_add_string(Pointer builder, long entid, String kw, String value);
|
||||||
|
RustResult in_progress_builder_add_long(Pointer builder, long entid, String kw, long value);
|
||||||
|
RustResult in_progress_builder_add_ref(Pointer builder, long entid, String kw, long value);
|
||||||
|
RustResult in_progress_builder_add_keyword(Pointer builder, long entid, String kw, String value);
|
||||||
|
RustResult in_progress_builder_add_timestamp(Pointer builder, long entid, String kw, long value);
|
||||||
|
RustResult in_progress_builder_add_boolean(Pointer builder, long entid, String kw, int value);
|
||||||
|
RustResult in_progress_builder_add_double(Pointer builder, long entid, String kw, double value);
|
||||||
|
RustResult in_progress_builder_add_uuid(Pointer builder, long entid, String kw, Pointer value);
|
||||||
|
RustResult in_progress_builder_retract_string(Pointer builder, long entid, String kw, String value);
|
||||||
|
RustResult in_progress_builder_retract_long(Pointer builder, long entid, String kw, long value);
|
||||||
|
RustResult in_progress_builder_retract_ref(Pointer builder, long entid, String kw, long value);
|
||||||
|
RustResult in_progress_builder_retract_keyword(Pointer builder, long entid, String kw, String value);
|
||||||
|
RustResult in_progress_builder_retract_timestamp(Pointer builder, long entid, String kw, long value);
|
||||||
|
RustResult in_progress_builder_retract_boolean(Pointer builder, long entid, String kw, int value);
|
||||||
|
RustResult in_progress_builder_retract_double(Pointer builder, long entid, String kw, double value);
|
||||||
|
RustResult in_progress_builder_retract_uuid(Pointer builder, long entid, String kw, Pointer value);
|
||||||
|
InProgressTransactionResult in_progress_builder_transact(Pointer builder);
|
||||||
|
RustResult in_progress_builder_commit(Pointer builder);
|
||||||
|
|
||||||
|
// entity building
|
||||||
|
RustResult store_entity_builder_from_temp_id(Pointer store, String temp_id);
|
||||||
|
RustResult store_entity_builder_from_entid(Pointer store, long entid);
|
||||||
|
RustResult entity_builder_add_string(Pointer builder, String kw, String value);
|
||||||
|
RustResult entity_builder_add_long(Pointer builder, String kw, long value);
|
||||||
|
RustResult entity_builder_add_ref(Pointer builder, String kw, long value);
|
||||||
|
RustResult entity_builder_add_keyword(Pointer builder, String kw, String value);
|
||||||
|
RustResult entity_builder_add_boolean(Pointer builder, String kw, int value);
|
||||||
|
RustResult entity_builder_add_double(Pointer builder, String kw, double value);
|
||||||
|
RustResult entity_builder_add_timestamp(Pointer builder, String kw, long value);
|
||||||
|
RustResult entity_builder_add_uuid(Pointer builder, String kw, Pointer value);
|
||||||
|
RustResult entity_builder_retract_string(Pointer builder, String kw, String value);
|
||||||
|
RustResult entity_builder_retract_long(Pointer builder, String kw, long value);
|
||||||
|
RustResult entity_builder_retract_ref(Pointer builder, String kw, long value);
|
||||||
|
RustResult entity_builder_retract_keyword(Pointer builder, String kw, String value);
|
||||||
|
RustResult entity_builder_retract_boolean(Pointer builder, String kw, int value);
|
||||||
|
RustResult entity_builder_retract_double(Pointer builder, String kw, double value);
|
||||||
|
RustResult entity_builder_retract_timestamp(Pointer builder, String kw, long value);
|
||||||
|
RustResult entity_builder_retract_uuid(Pointer builder, String kw, Pointer value);
|
||||||
|
InProgressTransactionResult entity_builder_transact(Pointer builder);
|
||||||
|
RustResult entity_builder_commit(Pointer builder);
|
||||||
|
|
||||||
// sync
|
// sync
|
||||||
RustResult store_sync(Pointer store, String userUuid, String serverUri);
|
RustResult store_sync(Pointer store, String userUuid, String serverUri);
|
||||||
|
@ -95,7 +155,7 @@ public interface JNA extends Library {
|
||||||
String value_at_index_into_kw(Pointer rows, int index);
|
String value_at_index_into_kw(Pointer rows, int index);
|
||||||
String value_at_index_into_string(Pointer rows, int index);
|
String value_at_index_into_string(Pointer rows, int index);
|
||||||
Pointer value_at_index_into_uuid(Pointer rows, int index);
|
Pointer value_at_index_into_uuid(Pointer rows, int index);
|
||||||
long value_at_index_into_boolean(Pointer rows, int index);
|
int value_at_index_into_boolean(Pointer rows, int index);
|
||||||
double value_at_index_into_double(Pointer rows, int index);
|
double value_at_index_into_double(Pointer rows, int index);
|
||||||
long value_at_index_into_timestamp(Pointer rows, int index);
|
long value_at_index_into_timestamp(Pointer rows, int index);
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,9 +48,38 @@ public class Mentat extends RustObject {
|
||||||
*/
|
*/
|
||||||
public Mentat(Pointer rawPointer) { this.rawPointer = rawPointer; }
|
public Mentat(Pointer rawPointer) { this.rawPointer = rawPointer; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an attribute to the cache. The {@link CacheDirection} determines how that attribute can be
|
||||||
|
* looked up.
|
||||||
|
*
|
||||||
|
* TODO: Throw an exception if cache action fails. https://github.com/mozilla/mentat/issues/700
|
||||||
|
*
|
||||||
|
* @param attribute The attribute to cache
|
||||||
|
* @param direction The direction the attribute should be keyed.
|
||||||
|
* FORWARD caches values for an attribute keyed by entity
|
||||||
|
* (i.e. find values and entities that have this attribute, or find values of attribute for an entity)
|
||||||
|
* REVERSE caches entities for an attribute keyed by value.
|
||||||
|
* (i.e. find entities that have a particular value for an attribute).
|
||||||
|
* BOTH adds an attribute such that it is cached in both directions.
|
||||||
|
*/
|
||||||
|
public void cache(String attribute, CacheDirection direction) {
|
||||||
|
RustResult result = null;
|
||||||
|
switch (direction) {
|
||||||
|
case FORWARD:
|
||||||
|
result = JNA.INSTANCE.store_cache_attribute_forward(this.rawPointer, attribute);
|
||||||
|
case REVERSE:
|
||||||
|
result = JNA.INSTANCE.store_cache_attribute_reverse(this.rawPointer, attribute);
|
||||||
|
case BOTH:
|
||||||
|
result = JNA.INSTANCE.store_cache_attribute_bi_directional(this.rawPointer, attribute);
|
||||||
|
}
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.e("Mentat", result.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple transact of an EDN string.
|
* Simple transact of an EDN string.
|
||||||
* TODO: Throw an exception if the transact fails
|
* TODO: Throw an exception if the transact fails. https://github.com/mozilla/mentat/issues/700
|
||||||
* @param transaction The string, as EDN, to be transacted.
|
* @param transaction The string, as EDN, to be transacted.
|
||||||
* @return The {@link TxReport} of the completed transaction
|
* @return The {@link TxReport} of the completed transaction
|
||||||
*/
|
*/
|
||||||
|
@ -88,7 +117,7 @@ public class Mentat extends RustObject {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve a single value of an attribute for an Entity
|
* Retrieve a single value of an attribute for an Entity
|
||||||
* TODO: Throw an exception if there is no the result contains an error.
|
* TODO: Throw an exception if the result contains an error. https://github.com/mozilla/mentat/issues/700
|
||||||
* @param attribute The string the attribute whose value is to be returned. The string is represented as `:namespace/name`.
|
* @param attribute The string the attribute whose value is to be returned. The string is represented as `:namespace/name`.
|
||||||
* @param entid The `Entid` of the entity we want the value from.
|
* @param entid The `Entid` of the entity we want the value from.
|
||||||
* @return The {@link TypedValue} containing the value of the attribute for the entity.
|
* @return The {@link TypedValue} containing the value of the attribute for the entity.
|
||||||
|
@ -138,6 +167,92 @@ public class Mentat extends RustObject {
|
||||||
JNA.INSTANCE.store_unregister_observer(rawPointer, key);
|
JNA.INSTANCE.store_unregister_observer(rawPointer, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start a new transaction
|
||||||
|
*
|
||||||
|
* TODO: Throw an exception if the result contains an error. https://github.com/mozilla/mentat/issues/700
|
||||||
|
*
|
||||||
|
* @return The {@link InProgress} used to manage the transaction
|
||||||
|
*/
|
||||||
|
public InProgress beginTransaction() {
|
||||||
|
RustResult result = JNA.INSTANCE.store_begin_transaction(this.rawPointer);
|
||||||
|
if (result.isSuccess()) {
|
||||||
|
return new InProgress(result.ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.i("Mentat", result.err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new transaction ({@link InProgress}) and returns an {@link InProgressBuilder} for
|
||||||
|
* that transaction.
|
||||||
|
*
|
||||||
|
* TODO: Throw an exception if the result contains an error. https://github.com/mozilla/mentat/issues/700
|
||||||
|
*
|
||||||
|
* @return an {@link InProgressBuilder} for a new transaction.
|
||||||
|
*/
|
||||||
|
public InProgressBuilder entityBuilder() {
|
||||||
|
RustResult result = JNA.INSTANCE.store_in_progress_builder(this.rawPointer);
|
||||||
|
if (result.isSuccess()) {
|
||||||
|
return new InProgressBuilder(result.ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.i("Mentat", result.err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new transaction ({@link InProgress}) and returns an {@link EntityBuilder} for the
|
||||||
|
* entity with `entid` for that transaction.
|
||||||
|
*
|
||||||
|
* TODO: Throw an exception if the result contains an error. https://github.com/mozilla/mentat/issues/700
|
||||||
|
*
|
||||||
|
* @param entid The `Entid` for this entity.
|
||||||
|
* @return an {@link EntityBuilder} for a new transaction.
|
||||||
|
*/
|
||||||
|
public EntityBuilder entityBuilder(long entid) {
|
||||||
|
RustResult result = JNA.INSTANCE.store_entity_builder_from_entid(this.rawPointer, entid);
|
||||||
|
if (result.isSuccess()) {
|
||||||
|
return new EntityBuilder(result.ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.i("Mentat", result.err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new transaction ({@link InProgress}) and returns an {@link EntityBuilder} for a new
|
||||||
|
* entity with `tempId` for that transaction.
|
||||||
|
*
|
||||||
|
* TODO: Throw an exception if the result contains an error. https://github.com/mozilla/mentat/issues/700
|
||||||
|
*
|
||||||
|
* @param tempId The temporary identifier for this entity.
|
||||||
|
* @return an {@link EntityBuilder} for a new transaction.
|
||||||
|
*/
|
||||||
|
public EntityBuilder entityBuilder(String tempId) {
|
||||||
|
RustResult result = JNA.INSTANCE.store_entity_builder_from_temp_id(this.rawPointer, tempId);
|
||||||
|
if (result.isSuccess()) {
|
||||||
|
return new EntityBuilder(result.ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.isFailure()) {
|
||||||
|
Log.i("Mentat", result.err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
if (this.rawPointer != null) {
|
if (this.rawPointer != null) {
|
||||||
|
|
|
@ -76,7 +76,7 @@ import java.util.UUID;
|
||||||
* <pre>{@code
|
* <pre>{@code
|
||||||
* String query = "[: find ?a .\n" +
|
* String query = "[: find ?a .\n" +
|
||||||
* " : where ... ]";
|
* " : where ... ]";
|
||||||
* mentat.query(query).runScalar(new ScalarResultHandler() {
|
* mentat.query(query).run(new ScalarResultHandler() {
|
||||||
* @Override
|
* @Override
|
||||||
* public void handleValue(TypedValue value) {
|
* public void handleValue(TypedValue value) {
|
||||||
* ...
|
* ...
|
||||||
|
@ -88,7 +88,7 @@ import java.util.UUID;
|
||||||
* <pre>{@code
|
* <pre>{@code
|
||||||
* String query = "[: find [?a ...]\n" +
|
* String query = "[: find [?a ...]\n" +
|
||||||
* " : where ... ]";
|
* " : where ... ]";
|
||||||
* mentat.query(query).runColl(new ScalarResultHandler() {
|
* mentat.query(query).run(new ScalarResultHandler() {
|
||||||
* @Override
|
* @Override
|
||||||
* public void handleList(CollResult list) {
|
* public void handleList(CollResult list) {
|
||||||
* ...
|
* ...
|
||||||
|
@ -100,7 +100,7 @@ import java.util.UUID;
|
||||||
* <pre>{@code
|
* <pre>{@code
|
||||||
* String query = "[: find [?a ?b ?c]\n" +
|
* String query = "[: find [?a ?b ?c]\n" +
|
||||||
* " : where ... ]";
|
* " : where ... ]";
|
||||||
* mentat.query(query).runTuple(new TupleResultHandler() {
|
* mentat.query(query).run(new TupleResultHandler() {
|
||||||
* @Override
|
* @Override
|
||||||
* public void handleRow(TupleResult row) {
|
* public void handleRow(TupleResult row) {
|
||||||
* ...
|
* ...
|
||||||
|
@ -121,7 +121,7 @@ public class Query extends RustObject {
|
||||||
* @param value The value to be bound
|
* @param value The value to be bound
|
||||||
* @return This {@link Query} such that further function can be called.
|
* @return This {@link Query} such that further function can be called.
|
||||||
*/
|
*/
|
||||||
Query bindLong(String varName, long value) {
|
Query bind(String varName, long value) {
|
||||||
this.validate();
|
this.validate();
|
||||||
JNA.INSTANCE.query_builder_bind_long(this.rawPointer, varName, value);
|
JNA.INSTANCE.query_builder_bind_long(this.rawPointer, varName, value);
|
||||||
return this;
|
return this;
|
||||||
|
@ -173,7 +173,7 @@ public class Query extends RustObject {
|
||||||
* @param value The value to be bound
|
* @param value The value to be bound
|
||||||
* @return This {@link Query} such that further function can be called.
|
* @return This {@link Query} such that further function can be called.
|
||||||
*/
|
*/
|
||||||
Query bindBoolean(String varName, boolean value) {
|
Query bind(String varName, boolean value) {
|
||||||
this.validate();
|
this.validate();
|
||||||
JNA.INSTANCE.query_builder_bind_boolean(this.rawPointer, varName, value ? 1 : 0);
|
JNA.INSTANCE.query_builder_bind_boolean(this.rawPointer, varName, value ? 1 : 0);
|
||||||
return this;
|
return this;
|
||||||
|
@ -186,7 +186,7 @@ public class Query extends RustObject {
|
||||||
* @param value The value to be bound
|
* @param value The value to be bound
|
||||||
* @return This {@link Query} such that further function can be called.
|
* @return This {@link Query} such that further function can be called.
|
||||||
*/
|
*/
|
||||||
Query bindDouble(String varName, double value) {
|
Query bind(String varName, double value) {
|
||||||
this.validate();
|
this.validate();
|
||||||
JNA.INSTANCE.query_builder_bind_double(this.rawPointer, varName, value);
|
JNA.INSTANCE.query_builder_bind_double(this.rawPointer, varName, value);
|
||||||
return this;
|
return this;
|
||||||
|
@ -199,7 +199,7 @@ public class Query extends RustObject {
|
||||||
* @param value The value to be bound
|
* @param value The value to be bound
|
||||||
* @return This {@link Query} such that further function can be called.
|
* @return This {@link Query} such that further function can be called.
|
||||||
*/
|
*/
|
||||||
Query bindDate(String varName, Date value) {
|
Query bind(String varName, Date value) {
|
||||||
this.validate();
|
this.validate();
|
||||||
long timestamp = value.getTime() * 1000;
|
long timestamp = value.getTime() * 1000;
|
||||||
JNA.INSTANCE.query_builder_bind_timestamp(this.rawPointer, varName, timestamp);
|
JNA.INSTANCE.query_builder_bind_timestamp(this.rawPointer, varName, timestamp);
|
||||||
|
@ -213,7 +213,7 @@ public class Query extends RustObject {
|
||||||
* @param value The value to be bound
|
* @param value The value to be bound
|
||||||
* @return This {@link Query} such that further function can be called.
|
* @return This {@link Query} such that further function can be called.
|
||||||
*/
|
*/
|
||||||
Query bindString(String varName, String value) {
|
Query bind(String varName, String value) {
|
||||||
this.validate();
|
this.validate();
|
||||||
JNA.INSTANCE.query_builder_bind_string(this.rawPointer, varName, value);
|
JNA.INSTANCE.query_builder_bind_string(this.rawPointer, varName, value);
|
||||||
return this;
|
return this;
|
||||||
|
@ -226,15 +226,9 @@ public class Query extends RustObject {
|
||||||
* @param value The value to be bound
|
* @param value The value to be bound
|
||||||
* @return This {@link Query} such that further function can be called.
|
* @return This {@link Query} such that further function can be called.
|
||||||
*/
|
*/
|
||||||
Query bindUUID(String varName, UUID value) {
|
Query bind(String varName, UUID value) {
|
||||||
this.validate();
|
this.validate();
|
||||||
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
|
JNA.INSTANCE.query_builder_bind_uuid(this.rawPointer, varName, getPointerForUUID(value));
|
||||||
bb.putLong(value.getMostSignificantBits());
|
|
||||||
bb.putLong(value.getLeastSignificantBits());
|
|
||||||
byte[] bytes = bb.array();
|
|
||||||
final Pointer bytesNativeArray = new Memory(bytes.length);
|
|
||||||
bytesNativeArray.write(0, bytes, 0, bytes.length);
|
|
||||||
JNA.INSTANCE.query_builder_bind_uuid(this.rawPointer, varName, bytesNativeArray);
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,7 +256,7 @@ public class Query extends RustObject {
|
||||||
* TODO: Throw an exception if the query raw pointer has been consumed or the query fails to execute
|
* TODO: Throw an exception if the query raw pointer has been consumed or the query fails to execute
|
||||||
* @param handler the handler to call with the results of this query
|
* @param handler the handler to call with the results of this query
|
||||||
*/
|
*/
|
||||||
void runScalar(final ScalarResultHandler handler) {
|
void run(final ScalarResultHandler handler) {
|
||||||
this.validate();
|
this.validate();
|
||||||
RustResult result = JNA.INSTANCE.query_builder_execute_scalar(rawPointer);
|
RustResult result = JNA.INSTANCE.query_builder_execute_scalar(rawPointer);
|
||||||
rawPointer = null;
|
rawPointer = null;
|
||||||
|
@ -285,7 +279,7 @@ public class Query extends RustObject {
|
||||||
* TODO: Throw an exception if the query raw pointer has been consumed or the query fails to execute
|
* TODO: Throw an exception if the query raw pointer has been consumed or the query fails to execute
|
||||||
* @param handler the handler to call with the results of this query
|
* @param handler the handler to call with the results of this query
|
||||||
*/
|
*/
|
||||||
void runColl(final CollResultHandler handler) {
|
void run(final CollResultHandler handler) {
|
||||||
this.validate();
|
this.validate();
|
||||||
RustResult result = JNA.INSTANCE.query_builder_execute_coll(rawPointer);
|
RustResult result = JNA.INSTANCE.query_builder_execute_coll(rawPointer);
|
||||||
rawPointer = null;
|
rawPointer = null;
|
||||||
|
@ -303,7 +297,7 @@ public class Query extends RustObject {
|
||||||
* TODO: Throw an exception if the query raw pointer has been consumed or the query fails to execute
|
* TODO: Throw an exception if the query raw pointer has been consumed or the query fails to execute
|
||||||
* @param handler the handler to call with the results of this query
|
* @param handler the handler to call with the results of this query
|
||||||
*/
|
*/
|
||||||
void runTuple(final TupleResultHandler handler) {
|
void run(final TupleResultHandler handler) {
|
||||||
this.validate();
|
this.validate();
|
||||||
RustResult result = JNA.INSTANCE.query_builder_execute_tuple(rawPointer);
|
RustResult result = JNA.INSTANCE.query_builder_execute_tuple(rawPointer);
|
||||||
rawPointer = null;
|
rawPointer = null;
|
||||||
|
|
|
@ -10,9 +10,12 @@
|
||||||
|
|
||||||
package com.mozilla.mentat;
|
package com.mozilla.mentat;
|
||||||
|
|
||||||
|
import com.sun.jna.Memory;
|
||||||
import com.sun.jna.Pointer;
|
import com.sun.jna.Pointer;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class that wraps an non-optional {@link Pointer} representing a pointer to a Rust object.
|
* Base class that wraps an non-optional {@link Pointer} representing a pointer to a Rust object.
|
||||||
|
@ -31,4 +34,22 @@ abstract class RustObject implements Closeable {
|
||||||
throw new NullPointerException(this.getClass() + " consumed");
|
throw new NullPointerException(this.getClass() + " consumed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Pointer getPointerForUUID(UUID uuid) {
|
||||||
|
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
|
||||||
|
bb.putLong(uuid.getMostSignificantBits());
|
||||||
|
bb.putLong(uuid.getLeastSignificantBits());
|
||||||
|
byte[] bytes = bb.array();
|
||||||
|
final Pointer bytesNativeArray = new Memory(bytes.length);
|
||||||
|
bytesNativeArray.write(0, bytes, 0, bytes.length);
|
||||||
|
return bytesNativeArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUID getUUIDFromPointer(Pointer uuidPtr) {
|
||||||
|
byte[] bytes = uuidPtr.getByteArray(0, 16);
|
||||||
|
ByteBuffer bb = ByteBuffer.wrap(bytes);
|
||||||
|
long high = bb.getLong();
|
||||||
|
long low = bb.getLong();
|
||||||
|
return new UUID(high, low);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,7 +126,7 @@ public class TupleResult extends RustObject {
|
||||||
*/
|
*/
|
||||||
public Date asDate(Integer index) {
|
public Date asDate(Integer index) {
|
||||||
this.validate();
|
this.validate();
|
||||||
return new Date(JNA.INSTANCE.value_at_index_into_timestamp(this.rawPointer, index));
|
return new Date(JNA.INSTANCE.value_at_index_into_timestamp(this.rawPointer, index) * 1_000);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -150,13 +150,7 @@ public class TupleResult extends RustObject {
|
||||||
*/
|
*/
|
||||||
public UUID asUUID(Integer index) {
|
public UUID asUUID(Integer index) {
|
||||||
this.validate();
|
this.validate();
|
||||||
Pointer uuidPtr = JNA.INSTANCE.value_at_index_into_uuid(this.rawPointer, index);
|
return getUUIDFromPointer(JNA.INSTANCE.value_at_index_into_uuid(this.rawPointer, index));
|
||||||
byte[] bytes = uuidPtr.getByteArray(0, 16);
|
|
||||||
ByteBuffer bb = ByteBuffer.wrap(bytes);
|
|
||||||
long high = bb.getLong();
|
|
||||||
long low = bb.getLong();
|
|
||||||
|
|
||||||
return new UUID(high, low);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -137,12 +137,7 @@ public class TypedValue extends RustObject {
|
||||||
*/
|
*/
|
||||||
public UUID asUUID() {
|
public UUID asUUID() {
|
||||||
if (!this.isConsumed()) {
|
if (!this.isConsumed()) {
|
||||||
Pointer uuidPtr = JNA.INSTANCE.typed_value_into_uuid(this.rawPointer);
|
this.value = getUUIDFromPointer(JNA.INSTANCE.typed_value_into_uuid(this.rawPointer));
|
||||||
byte[] bytes = uuidPtr.getByteArray(0, 16);
|
|
||||||
ByteBuffer bb = ByteBuffer.wrap(bytes);
|
|
||||||
long high = bb.getLong();
|
|
||||||
long low = bb.getLong();
|
|
||||||
this.value = new UUID(high, low);
|
|
||||||
this.rawPointer = null;
|
this.rawPointer = null;
|
||||||
}
|
}
|
||||||
return (UUID)this.value;
|
return (UUID)this.value;
|
||||||
|
|
Binary file not shown.
|
@ -7,6 +7,9 @@
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
7B64E44D209094520063909F /* InProgressBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B64E44A209094510063909F /* InProgressBuilder.swift */; };
|
||||||
|
7B64E44E209094520063909F /* EntityBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B64E44B209094510063909F /* EntityBuilder.swift */; };
|
||||||
|
7B64E44F209094520063909F /* InProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B64E44C209094520063909F /* InProgress.swift */; };
|
||||||
7B74483D208DF667006CFFB0 /* Result+Unwrap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B74483C208DF667006CFFB0 /* Result+Unwrap.swift */; };
|
7B74483D208DF667006CFFB0 /* Result+Unwrap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B74483C208DF667006CFFB0 /* Result+Unwrap.swift */; };
|
||||||
7BAE75A22089020E00895D37 /* libmentat_ffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7BEB7D23207BE2AF000369AD /* libmentat_ffi.a */; };
|
7BAE75A22089020E00895D37 /* libmentat_ffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7BEB7D23207BE2AF000369AD /* libmentat_ffi.a */; };
|
||||||
7BAE75A42089022B00895D37 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 7BAE75A32089022B00895D37 /* libsqlite3.tbd */; };
|
7BAE75A42089022B00895D37 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 7BAE75A32089022B00895D37 /* libsqlite3.tbd */; };
|
||||||
|
@ -39,6 +42,9 @@
|
||||||
/* End PBXContainerItemProxy section */
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
7B64E44A209094510063909F /* InProgressBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InProgressBuilder.swift; sourceTree = "<group>"; };
|
||||||
|
7B64E44B209094510063909F /* EntityBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EntityBuilder.swift; sourceTree = "<group>"; };
|
||||||
|
7B64E44C209094520063909F /* InProgress.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InProgress.swift; sourceTree = "<group>"; };
|
||||||
7B74483C208DF667006CFFB0 /* Result+Unwrap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "Result+Unwrap.swift"; path = "Mentat/Extensions/Result+Unwrap.swift"; sourceTree = SOURCE_ROOT; };
|
7B74483C208DF667006CFFB0 /* Result+Unwrap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "Result+Unwrap.swift"; path = "Mentat/Extensions/Result+Unwrap.swift"; sourceTree = SOURCE_ROOT; };
|
||||||
7B911E1A2085081D000998CB /* libtoodle.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtoodle.a; path = "../../../../sync-storage-prototype/rust/target/universal/release/libtoodle.a"; sourceTree = "<group>"; };
|
7B911E1A2085081D000998CB /* libtoodle.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtoodle.a; path = "../../../../sync-storage-prototype/rust/target/universal/release/libtoodle.a"; sourceTree = "<group>"; };
|
||||||
7BAE75A32089022B00895D37 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; };
|
7BAE75A32089022B00895D37 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; };
|
||||||
|
@ -196,6 +202,9 @@
|
||||||
7BEB7D26207BE5BB000369AD /* Transact */ = {
|
7BEB7D26207BE5BB000369AD /* Transact */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
7B64E44B209094510063909F /* EntityBuilder.swift */,
|
||||||
|
7B64E44C209094520063909F /* InProgress.swift */,
|
||||||
|
7B64E44A209094510063909F /* InProgressBuilder.swift */,
|
||||||
7BEB7D2B207D03DA000369AD /* TxReport.swift */,
|
7BEB7D2B207D03DA000369AD /* TxReport.swift */,
|
||||||
);
|
);
|
||||||
path = Transact;
|
path = Transact;
|
||||||
|
@ -321,8 +330,11 @@
|
||||||
7BDB96CC207B7684009D0651 /* Errors.swift in Sources */,
|
7BDB96CC207B7684009D0651 /* Errors.swift in Sources */,
|
||||||
7BDB96B02077C38E009D0651 /* Mentat.swift in Sources */,
|
7BDB96B02077C38E009D0651 /* Mentat.swift in Sources */,
|
||||||
7BDB96B72077C38E009D0651 /* TypedValue.swift in Sources */,
|
7BDB96B72077C38E009D0651 /* TypedValue.swift in Sources */,
|
||||||
|
7B64E44E209094520063909F /* EntityBuilder.swift in Sources */,
|
||||||
|
7B64E44D209094520063909F /* InProgressBuilder.swift in Sources */,
|
||||||
7BDB96B52077C38E009D0651 /* TupleResult.swift in Sources */,
|
7BDB96B52077C38E009D0651 /* TupleResult.swift in Sources */,
|
||||||
7B74483D208DF667006CFFB0 /* Result+Unwrap.swift in Sources */,
|
7B74483D208DF667006CFFB0 /* Result+Unwrap.swift in Sources */,
|
||||||
|
7B64E44F209094520063909F /* InProgress.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -37,14 +37,6 @@
|
||||||
BlueprintName = "MentatTests"
|
BlueprintName = "MentatTests"
|
||||||
ReferencedContainer = "container:Mentat.xcodeproj">
|
ReferencedContainer = "container:Mentat.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
<SkippedTests>
|
|
||||||
<Test
|
|
||||||
Identifier = "MentatTests/testBindBoolean()">
|
|
||||||
</Test>
|
|
||||||
<Test
|
|
||||||
Identifier = "MentatTests/testBindDate()">
|
|
||||||
</Test>
|
|
||||||
</SkippedTests>
|
|
||||||
</TestableReference>
|
</TestableReference>
|
||||||
</Testables>
|
</Testables>
|
||||||
<MacroExpansion>
|
<MacroExpansion>
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
//
|
|
||||||
/* Copyright 2018 Mozilla
|
/* Copyright 2018 Mozilla
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
|
|
@ -29,6 +29,12 @@ protocol Observable {
|
||||||
func unregister(key: String)
|
func unregister(key: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum CacheDirection {
|
||||||
|
case forward;
|
||||||
|
case reverse;
|
||||||
|
case both;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The primary class for accessing Mentat's API.
|
The primary class for accessing Mentat's API.
|
||||||
This class provides all of the basic API that can be found in Mentat's Store struct.
|
This class provides all of the basic API that can be found in Mentat's Store struct.
|
||||||
|
@ -56,11 +62,36 @@ class Mentat: RustObject {
|
||||||
self.init(raw: store_open(storeURI))
|
self.init(raw: store_open(storeURI))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Add an attribute to the cache. The {@link CacheDirection} determines how that attribute can be
|
||||||
|
looked up.
|
||||||
|
|
||||||
|
- Parameter attribute: The attribute to cache
|
||||||
|
- Parameter direction: The direction the attribute should be keyed.
|
||||||
|
`forward` caches values for an attribute keyed by entity
|
||||||
|
(i.e. find values and entities that have this attribute, or find values of attribute for an entity)
|
||||||
|
`reverse` caches entities for an attribute keyed by value.
|
||||||
|
(i.e. find entities that have a particular value for an attribute).
|
||||||
|
`both` adds an attribute such that it is cached in both directions.
|
||||||
|
|
||||||
|
- Throws: `ResultError.error` if an error occured while trying to cache the attribute.
|
||||||
|
*/
|
||||||
|
func cache(attribute: String, direction: CacheDirection) throws {
|
||||||
|
switch direction {
|
||||||
|
case .forward:
|
||||||
|
try store_cache_attribute_forward(self.raw, attribute).pointee.tryUnwrap()
|
||||||
|
case .reverse:
|
||||||
|
try store_cache_attribute_reverse(self.raw, attribute).pointee.tryUnwrap()
|
||||||
|
case .both:
|
||||||
|
try store_cache_attribute_bi_directional(self.raw, attribute).pointee.tryUnwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Simple transact of an EDN string.
|
Simple transact of an EDN string.
|
||||||
- Parameter transaction: The string, as EDN, to be transacted
|
- Parameter transaction: The string, as EDN, to be transacted
|
||||||
|
|
||||||
- Throws: `MentatError` if the an error occured during the transaction, or the TxReport is nil.
|
- Throws: `ResultError.error` if the an error occured during the transaction, or the TxReport is nil.
|
||||||
|
|
||||||
- Returns: The `TxReport` of the completed transaction
|
- Returns: The `TxReport` of the completed transaction
|
||||||
*/
|
*/
|
||||||
|
@ -69,8 +100,67 @@ class Mentat: RustObject {
|
||||||
return TxReport(raw: try result.unwrap())
|
return TxReport(raw: try result.unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Start a new transaction.
|
||||||
|
|
||||||
|
- Throws: `ResultError.error` if the creation of the transaction fails.
|
||||||
|
- Throws: `ResultError.empty` if no `InProgress` is created.
|
||||||
|
|
||||||
|
- Returns: The `InProgress` used to manage the transaction
|
||||||
|
*/
|
||||||
|
func beginTransaction() throws -> InProgress {
|
||||||
|
let result = store_begin_transaction(self.raw).pointee;
|
||||||
|
return InProgress(raw: try result.unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Creates a new transaction (`InProgress`) and returns an `InProgressBuilder` for that transaction.
|
||||||
|
|
||||||
|
- Throws: `ResultError.error` if the creation of the transaction fails.
|
||||||
|
- Throws: `ResultError.empty` if no `InProgressBuilder` is created.
|
||||||
|
|
||||||
|
- Returns: an `InProgressBuilder` for this `InProgress`
|
||||||
|
*/
|
||||||
|
func entityBuilder() throws -> InProgressBuilder {
|
||||||
|
let result = store_in_progress_builder(self.raw).pointee
|
||||||
|
return InProgressBuilder(raw: try result.unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Creates a new transaction (`InProgress`) and returns an `EntityBuilder` for the entity with `entid`
|
||||||
|
for that transaction.
|
||||||
|
|
||||||
|
- Parameter entid: The `Entid` for this entity.
|
||||||
|
|
||||||
|
- Throws: `ResultError.error` if the creation of the transaction fails.
|
||||||
|
- Throws: `ResultError.empty` if no `EntityBuilder` is created.
|
||||||
|
|
||||||
|
- Returns: an `EntityBuilder` for this `InProgress`
|
||||||
|
*/
|
||||||
|
func entityBuilder(forEntid entid: Entid) throws -> EntityBuilder {
|
||||||
|
let result = store_entity_builder_from_entid(self.raw, entid).pointee
|
||||||
|
return EntityBuilder(raw: try result.unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Creates a new transaction (`InProgress`) and returns an `EntityBuilder` for a new entity with `tempId`
|
||||||
|
for that transaction.
|
||||||
|
|
||||||
|
- Parameter tempId: The temporary identifier for this entity.
|
||||||
|
|
||||||
|
- Throws: `ResultError.error` if the creation of the transaction fails.
|
||||||
|
- Throws: `ResultError.empty` if no `EntityBuilder` is created.
|
||||||
|
|
||||||
|
- Returns: an `EntityBuilder` for this `InProgress`
|
||||||
|
*/
|
||||||
|
func entityBuilder(forTempId tempId: String) throws -> EntityBuilder {
|
||||||
|
let result = store_entity_builder_from_temp_id(self.raw, tempId).pointee
|
||||||
|
return EntityBuilder(raw: try result.unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Get the the `Entid` of the attribute.
|
Get the the `Entid` of the attribute.
|
||||||
|
|
||||||
- Parameter attribute: The string represeting the attribute whose `Entid` we are after.
|
- Parameter attribute: The string represeting the attribute whose `Entid` we are after.
|
||||||
The string is represented as `:namespace/name`.
|
The string is represented as `:namespace/name`.
|
||||||
|
|
||||||
|
|
355
sdks/swift/Mentat/Mentat/Transact/EntityBuilder.swift
Normal file
355
sdks/swift/Mentat/Mentat/Transact/EntityBuilder.swift
Normal file
|
@ -0,0 +1,355 @@
|
||||||
|
/* Copyright 2018 Mozilla
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License. */
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import MentatStore
|
||||||
|
|
||||||
|
/**
|
||||||
|
This class wraps a raw pointer that points to a Rust `EntityBuilder<InProgressBuilder>` object.
|
||||||
|
|
||||||
|
`EntityBuilder` provides a programmatic interface to performing assertions on a specific entity.
|
||||||
|
It provides functions for adding and retracting values for attributes for an entity within
|
||||||
|
an in progress transaction.
|
||||||
|
|
||||||
|
The `transact` function will transact the assertions that have been added to the `EntityBuilder`
|
||||||
|
and pass back the `TxReport` that was generated by this transact and the `InProgress` that was
|
||||||
|
used to perform the transact. This enables you to perform further transacts on the same `InProgress`
|
||||||
|
before committing.
|
||||||
|
|
||||||
|
```
|
||||||
|
let aEntid = txReport.entid(forTempId: "a")
|
||||||
|
let bEntid = txReport.entid(forTempId: "b")
|
||||||
|
do {
|
||||||
|
let builder = try mentat.entityBuilder(forEntid: bEntid)
|
||||||
|
try builder.add(keyword: ":foo/boolean", boolean: true)
|
||||||
|
try builder.add(keyword: ":foo/instant", date: newDate)
|
||||||
|
let (inProgress, report) = try builder.transact()
|
||||||
|
try inProgress.transact(transaction: "[[:db/add \(aEntid) :foo/long 22]]")
|
||||||
|
try inProgress.commit()
|
||||||
|
} catch {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `commit` function will transact and commit the assertions that have been added to the `EntityBuilder`.
|
||||||
|
It will consume the `InProgress` used to perform the transact. It returns the `TxReport` generated by
|
||||||
|
the transact. After calling `commit`, a new transaction must be started by calling `Mentat.beginTransaction()`
|
||||||
|
in order to perform further actions.
|
||||||
|
|
||||||
|
```
|
||||||
|
let aEntid = txReport.entid(forTempId: "a")
|
||||||
|
do {
|
||||||
|
let builder = try mentat.entityBuilder(forEntid: aEntid)
|
||||||
|
try builder.add(keyword: ":foo/boolean", boolean: true)
|
||||||
|
try builder.add(keyword: ":foo/instant", date: newDate)
|
||||||
|
let report = try builder.commit()
|
||||||
|
...
|
||||||
|
} catch {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
class EntityBuilder: OptionalRustObject {
|
||||||
|
/**
|
||||||
|
Asserts the value of attribute `keyword` to be the provided `value`.
|
||||||
|
|
||||||
|
- Parameter keyword: The name of the attribute in the format `:namespace/name`.
|
||||||
|
- Parameter value: The value to be asserted
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if the attribute is not present in the schema or the attribute value type
|
||||||
|
is not `:db.type/long`.
|
||||||
|
*/
|
||||||
|
func add(keyword: String, long value: Int64) throws {
|
||||||
|
try entity_builder_add_long(try self.validPointer(), keyword, value).pointee.tryUnwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Asserts the value of attribute `keyword` to be the provided `value`.
|
||||||
|
|
||||||
|
- Parameter keyword: The name of the attribute in the format `:namespace/name`.
|
||||||
|
- Parameter value: The value to be asserted
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if the attribute is not present in the schema or the attribute value type
|
||||||
|
is not `:db.type/ref`.
|
||||||
|
*/
|
||||||
|
func add(keyword: String, reference value: Int64) throws {
|
||||||
|
try entity_builder_add_ref(try self.validPointer(), keyword, value).pointee.tryUnwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Asserts the value of attribute `keyword` to be the provided `value`.
|
||||||
|
|
||||||
|
- Parameter keyword: The name of the attribute in the format `:namespace/name`.
|
||||||
|
- Parameter value: The value to be asserted
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if the attribute is not present in the schema or the attribute value type
|
||||||
|
is not `:db.type/keyword`.
|
||||||
|
*/
|
||||||
|
func add(keyword: String, keyword value: String) throws {
|
||||||
|
try entity_builder_add_keyword(try self.validPointer(), keyword, value).pointee.tryUnwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Asserts the value of attribute `keyword` to be the provided `value`.
|
||||||
|
|
||||||
|
- Parameter keyword: The name of the attribute in the format `:namespace/name`.
|
||||||
|
- Parameter value: The value to be asserted
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if the attribute is not present in the schema or the attribute value type
|
||||||
|
is not `:db.type/boolean`.
|
||||||
|
*/
|
||||||
|
func add(keyword: String, boolean value: Bool) throws {
|
||||||
|
try entity_builder_add_boolean(try self.validPointer(), keyword, value ? 1 : 0).pointee.tryUnwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Asserts the value of attribute `keyword` to be the provided `value`.
|
||||||
|
|
||||||
|
- Parameter keyword: The name of the attribute in the format `:namespace/name`.
|
||||||
|
- Parameter value: The value to be asserted
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if the attribute is not present in the schema or the attribute value type
|
||||||
|
is not `:db.type/double`.
|
||||||
|
*/
|
||||||
|
func add(keyword: String, double value: Double) throws {
|
||||||
|
try entity_builder_add_double(try self.validPointer(), keyword, value).pointee.tryUnwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Asserts the value of attribute `keyword` to be the provided `value`.
|
||||||
|
|
||||||
|
- Parameter keyword: The name of the attribute in the format `:namespace/name`.
|
||||||
|
- Parameter value: The value to be asserted
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
|
||||||
|
- Throws: `ResultError.error` if the attribute is not present in the schema or the attribute value type
|
||||||
|
is not `:db.type/instant`.
|
||||||
|
*/
|
||||||
|
func add(keyword: String, date value: Date) throws {
|
||||||
|
try entity_builder_add_timestamp(try self.validPointer(), keyword, value.toMicroseconds()).pointee.tryUnwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Asserts the value of attribute `keyword` to be the provided `value`.
|
||||||
|
|
||||||
|
- Parameter keyword: The name of the attribute in the format `:namespace/name`.
|
||||||
|
- Parameter value: The value to be asserted
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if the attribute is not present in the schema or the attribute value type
|
||||||
|
is not `:db.type/string`.
|
||||||
|
*/
|
||||||
|
func add(keyword: String, string value: String) throws {
|
||||||
|
try entity_builder_add_string(try self.validPointer(), keyword, value).pointee.tryUnwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Asserts the value of attribute `keyword` to be the provided `value`.
|
||||||
|
|
||||||
|
- Parameter keyword: The name of the attribute in the format `:namespace/name`.
|
||||||
|
- Parameter value: The value to be asserted
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if the attribute is not present in the schema or the attribute value type
|
||||||
|
is not `:db.type/uuid`.
|
||||||
|
*/
|
||||||
|
func add(keyword: String, uuid value: UUID) throws {
|
||||||
|
var rawUuid = value.uuid
|
||||||
|
let _ = try withUnsafePointer(to: &rawUuid) { uuidPtr in
|
||||||
|
try entity_builder_add_uuid(try self.validPointer(), keyword, uuidPtr).pointee.tryUnwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Retracts the value of attribute `keyword` from the provided `value`.
|
||||||
|
|
||||||
|
- Parameter keyword: The name of the attribute in the format `:namespace/name`.
|
||||||
|
- Parameter value: The value to be retracted
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if the attribute is not present in the schema or the attribute value type
|
||||||
|
is not `:db.type/long`.
|
||||||
|
*/
|
||||||
|
func retract(keyword: String, long value: Int64) throws {
|
||||||
|
try entity_builder_retract_long(try self.validPointer(), keyword, value).pointee.tryUnwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Retracts the value of attribute `keyword` from the provided `value`.
|
||||||
|
|
||||||
|
- Parameter keyword: The name of the attribute in the format `:namespace/name`.
|
||||||
|
- Parameter value: The value to be retracted
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if the attribute is not present in the schema or the attribute value type
|
||||||
|
is not `:db.type/ref`.
|
||||||
|
*/
|
||||||
|
func retract(keyword: String, reference value: Int64) throws {
|
||||||
|
try entity_builder_retract_ref(try self.validPointer(), keyword, value).pointee.tryUnwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Retracts the value of attribute `keyword` from the provided `value`.
|
||||||
|
|
||||||
|
- Parameter keyword: The name of the attribute in the format `:namespace/name`.
|
||||||
|
- Parameter value: The value to be retracted
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if the attribute is not present in the schema or the attribute value type
|
||||||
|
is not `:db.type/keyword`.
|
||||||
|
*/
|
||||||
|
func retract(keyword: String, keyword value: String) throws {
|
||||||
|
try entity_builder_retract_keyword(try self.validPointer(), keyword, value).pointee.tryUnwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Retracts the value of attribute `keyword` from the provided `value`.
|
||||||
|
|
||||||
|
- Parameter keyword: The name of the attribute in the format `:namespace/name`.
|
||||||
|
- Parameter value: The value to be retracted
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if the attribute is not present in the schema or the attribute value type
|
||||||
|
is not `:db.type/boolean`.
|
||||||
|
*/
|
||||||
|
func retract(keyword: String, boolean value: Bool) throws {
|
||||||
|
try entity_builder_retract_boolean(try self.validPointer(), keyword, value ? 1 : 0).pointee.tryUnwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Retracts the value of attribute `keyword` from the provided `value`.
|
||||||
|
|
||||||
|
- Parameter keyword: The name of the attribute in the format `:namespace/name`.
|
||||||
|
- Parameter value: The value to be retracted
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if the attribute is not present in the schema or the attribute value type
|
||||||
|
is not `:db.type/double`.
|
||||||
|
*/
|
||||||
|
func retract(keyword: String, double value: Double) throws {
|
||||||
|
try entity_builder_retract_double(try self.validPointer(), keyword, value).pointee.tryUnwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Retracts the value of attribute `keyword` from the provided `value`.
|
||||||
|
|
||||||
|
- Parameter keyword: The name of the attribute in the format `:namespace/name`.
|
||||||
|
- Parameter value: The value to be retracted
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if the attribute is not present in the schema or the attribute value type
|
||||||
|
is not `:db.type/instant`.
|
||||||
|
*/
|
||||||
|
func retract(keyword: String, date value: Date) throws {
|
||||||
|
try entity_builder_retract_timestamp(try self.validPointer(), keyword, value.toMicroseconds()).pointee.tryUnwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Retracts the value of attribute `keyword` from the provided `value`.
|
||||||
|
|
||||||
|
- Parameter keyword: The name of the attribute in the format `:namespace/name`.
|
||||||
|
- Parameter value: The value to be retracted
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if the attribute is not present in the schema or the attribute value type
|
||||||
|
is not `:db.type/string`.
|
||||||
|
*/
|
||||||
|
func retract(keyword: String, string value: String) throws {
|
||||||
|
try entity_builder_retract_string(try self.validPointer(), keyword, value).pointee.tryUnwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Retracts the value of attribute `keyword` from the provided `value`.
|
||||||
|
|
||||||
|
- Parameter keyword: The name of the attribute in the format `:namespace/name`.
|
||||||
|
- Parameter value: The value to be retracted
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if the attribute is not present in the schema or the attribute value type
|
||||||
|
is not `:db.type/uuid`.
|
||||||
|
*/
|
||||||
|
func retract(keyword: String, uuid value: UUID) throws {
|
||||||
|
var rawUuid = value.uuid
|
||||||
|
let _ = try withUnsafePointer(to: &rawUuid) { uuidPtr in
|
||||||
|
try entity_builder_retract_uuid(try self.validPointer(), keyword, uuidPtr).pointee.tryUnwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Transacts the added assertions. This consumes the pointer associated with this `EntityBuilder`
|
||||||
|
such that no further assertions can be added after the `transact` has completed. To perform
|
||||||
|
further assertions, use the `InProgress` returned from this function.
|
||||||
|
|
||||||
|
This does not commit the transaction. In order to do so, `commit` can be called on the `InProgress` returned
|
||||||
|
from this function.
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if an error occured during the execution of the transact.
|
||||||
|
|
||||||
|
- Returns: The current `InProgress` and the `TxReport` generated by the transact.
|
||||||
|
*/
|
||||||
|
func transact() throws -> (InProgress, TxReport?) {
|
||||||
|
defer {
|
||||||
|
self.raw = nil
|
||||||
|
}
|
||||||
|
let result = entity_builder_transact(try self.validPointer()).pointee
|
||||||
|
let inProgress = InProgress(raw: result.inProgress)
|
||||||
|
guard let report = try result.result.pointee.tryUnwrap() else {
|
||||||
|
return (inProgress, nil)
|
||||||
|
}
|
||||||
|
return (inProgress, TxReport(raw: report))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Transacts the added assertions and commits. This consumes the pointer associated with this `EntityBuilder`
|
||||||
|
and the associated `InProgress` such that no further assertions can be added after the `commit` has completed.
|
||||||
|
To perform further assertions, a new `InProgress` or `EntityBuilder` should be created.
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if an error occured during the execution of the transact.
|
||||||
|
|
||||||
|
- Returns: The `TxReport` generated by the transact.
|
||||||
|
*/
|
||||||
|
func commit() throws -> TxReport {
|
||||||
|
defer {
|
||||||
|
self.raw = nil
|
||||||
|
}
|
||||||
|
return TxReport(raw: try entity_builder_commit(try self.validPointer()).pointee.unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
override func cleanup(pointer: OpaquePointer) {
|
||||||
|
entity_builder_destroy(pointer)
|
||||||
|
}
|
||||||
|
}
|
184
sdks/swift/Mentat/Mentat/Transact/InProgress.swift
Normal file
184
sdks/swift/Mentat/Mentat/Transact/InProgress.swift
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
/* Copyright 2018 Mozilla
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License. */
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import MentatStore
|
||||||
|
|
||||||
|
/**
|
||||||
|
This class wraps a raw pointer that points to a Rust `InProgress` object.
|
||||||
|
|
||||||
|
`InProgress` allows for multiple transacts to be performed in a single transaction.
|
||||||
|
Each transact performed results in a `TxReport` that can be used to gather information
|
||||||
|
to be used in subsequent transacts.
|
||||||
|
|
||||||
|
Committing an `InProgress` commits all the transacts that have been performed using
|
||||||
|
that `InProgress`.
|
||||||
|
|
||||||
|
Rolling back and `InProgress` rolls back all the transacts that have been performed
|
||||||
|
using that `InProgress`.
|
||||||
|
|
||||||
|
```
|
||||||
|
do {
|
||||||
|
let inProgress = try mentat.beginTransaction()
|
||||||
|
let txReport = try inProgress.transact(transaction: "[[:db/add "a" :foo/long 22]]")
|
||||||
|
let aEntid = txReport.entid(forTempId: "a")
|
||||||
|
let report = try inProgress.transact(transaction: "[[:db/add "b" :foo/ref \(aEntid)] [:db/add "b" :foo/boolean true]]")
|
||||||
|
try inProgress.commit()
|
||||||
|
} catch {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`InProgress` also provides a number of functions to generating an builder to assert datoms programatically.
|
||||||
|
The two types of builder are `InProgressBuilder` and `EntityBuilder`.
|
||||||
|
|
||||||
|
`InProgressBuilder` takes the current `InProgress` and provides a programmatic interface to add
|
||||||
|
and retract values from entities for which there exists an `Entid`. The provided `InProgress`
|
||||||
|
is used to perform the transacts.
|
||||||
|
|
||||||
|
```
|
||||||
|
let aEntid = txReport.entid(forTempId: "a")
|
||||||
|
let bEntid = txReport.entid(forTempId: "b")
|
||||||
|
do {
|
||||||
|
let inProgress = try mentat.beginTransaction()
|
||||||
|
let builder = try inProgress.builder()
|
||||||
|
try builder.add(entid: bEntid, keyword: ":foo/boolean", boolean: true)
|
||||||
|
try builder.add(entid: aEntid, keyword: ":foo/instant", date: newDate)
|
||||||
|
let (inProgress, report) = try builder.transact()
|
||||||
|
try inProgress.transact(transaction: "[[:db/add \(aEntid) :foo/long 22]]")
|
||||||
|
try inProgress.commit()
|
||||||
|
} catch {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`EntityBuilder` takes the current `InProgress` and either an `Entid` or a `tempid` to provide
|
||||||
|
a programmatic interface to add and retract values from a specific entity. The provided `InProgress`
|
||||||
|
is used to perform the transacts.
|
||||||
|
|
||||||
|
```
|
||||||
|
do {
|
||||||
|
let transaction = try mentat.beginTransaction()
|
||||||
|
let builder = try transaction.builder(forTempId: "b")
|
||||||
|
try builder.add(keyword: ":foo/boolean", boolean: true)
|
||||||
|
try builder.add(keyword: ":foo/instant", date: newDate)
|
||||||
|
let (inProgress, report) = try builder.transact()
|
||||||
|
let bEntid = report.entid(forTempId: "b")
|
||||||
|
try inProgress.transact(transaction: "[[:db/add \(bEntid) :foo/long 22]]")
|
||||||
|
try inProgress.commit()
|
||||||
|
} catch {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
class InProgress: OptionalRustObject {
|
||||||
|
|
||||||
|
/**
|
||||||
|
Creates an `InProgressBuilder` using this `InProgress`.
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the `InProgress`
|
||||||
|
has already been committed, or converted into a Builder.
|
||||||
|
|
||||||
|
- Returns: an `InProgressBuilder` for this `InProgress`
|
||||||
|
*/
|
||||||
|
func builder() throws -> InProgressBuilder {
|
||||||
|
defer {
|
||||||
|
self.raw = nil
|
||||||
|
}
|
||||||
|
return InProgressBuilder(raw: in_progress_builder(try self.validPointer()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Creates an `EntityBuilder` using this `InProgress` for the entity with `entid`.
|
||||||
|
|
||||||
|
- Parameter entid: The `Entid` for this entity.
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the `InProgress`
|
||||||
|
has already been committed, or converted into a Builder.
|
||||||
|
|
||||||
|
- Returns: an `EntityBuilder` for this `InProgress`
|
||||||
|
*/
|
||||||
|
func builder(forEntid entid: Int64) throws -> EntityBuilder {
|
||||||
|
defer {
|
||||||
|
self.raw = nil
|
||||||
|
}
|
||||||
|
return EntityBuilder(raw: in_progress_entity_builder_from_entid(try self.validPointer(), entid))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Creates an `EntityBuilder` using this `InProgress` for a new entity with `tempId`.
|
||||||
|
|
||||||
|
- Parameter tempId: The temporary identifier for this entity.
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the `InProgress`
|
||||||
|
has already been committed, or converted into a Builder.
|
||||||
|
|
||||||
|
- Returns: an `EntityBuilder` for this `InProgress`
|
||||||
|
*/
|
||||||
|
func builder(forTempId tempId: String) throws -> EntityBuilder {
|
||||||
|
defer {
|
||||||
|
self.raw = nil
|
||||||
|
}
|
||||||
|
return EntityBuilder(raw: in_progress_entity_builder_from_temp_id(try self.validPointer(), tempId))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Transacts the `transaction`
|
||||||
|
|
||||||
|
This does not commit the transaction. In order to do so, `commit` can be called.
|
||||||
|
|
||||||
|
- Parameter transaction: The EDN string to be transacted.
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if the transaction failed.
|
||||||
|
- Throws: `ResultError.empty` if no `TxReport` is returned from the transact.
|
||||||
|
|
||||||
|
- Returns: The `TxReport` generated by the transact.
|
||||||
|
*/
|
||||||
|
func transact(transaction: String) throws -> TxReport {
|
||||||
|
let result = in_progress_transact(try self.validPointer(), transaction).pointee
|
||||||
|
return TxReport(raw: try result.unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Commits all the transacts that have been performed on this `InProgress`, either directly
|
||||||
|
or through a Builder.
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if the commit failed.
|
||||||
|
*/
|
||||||
|
func commit() throws {
|
||||||
|
defer {
|
||||||
|
self.raw = nil
|
||||||
|
}
|
||||||
|
try in_progress_commit(try self.validPointer()).pointee.tryUnwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Rolls back all the transacts that have been performed on this `InProgress`, either directly
|
||||||
|
or through a Builder.
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if the rollback failed.
|
||||||
|
*/
|
||||||
|
func rollback() throws {
|
||||||
|
defer {
|
||||||
|
self.raw = nil
|
||||||
|
}
|
||||||
|
try in_progress_rollback(try self.validPointer()).pointee.tryUnwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func cleanup(pointer: OpaquePointer) {
|
||||||
|
in_progress_destroy(pointer)
|
||||||
|
}
|
||||||
|
}
|
372
sdks/swift/Mentat/Mentat/Transact/InProgressBuilder.swift
Normal file
372
sdks/swift/Mentat/Mentat/Transact/InProgressBuilder.swift
Normal file
|
@ -0,0 +1,372 @@
|
||||||
|
/* Copyright 2018 Mozilla
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License. */
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import MentatStore
|
||||||
|
|
||||||
|
/**
|
||||||
|
This class wraps a raw pointer that points to a Rust `InProgressBuilder` object.
|
||||||
|
|
||||||
|
`InProgressBuilder` provides a programmatic interface to performing assertions for entities.
|
||||||
|
It provides functions for adding and retracting values for attributes for an entity within
|
||||||
|
an in progress transaction.
|
||||||
|
|
||||||
|
The `transact` function will transact the assertions that have been added to the `InProgressBuilder`
|
||||||
|
and pass back the `TxReport` that was generated by this transact and the `InProgress` that was
|
||||||
|
used to perform the transact. This enables you to perform further transacts on the same `InProgress`
|
||||||
|
before committing.
|
||||||
|
|
||||||
|
```
|
||||||
|
let aEntid = txReport.entid(forTempId: "a")
|
||||||
|
let bEntid = txReport.entid(forTempId: "b")
|
||||||
|
do {
|
||||||
|
let builder = try mentat.entityBuilder()
|
||||||
|
try builder.add(entid: bEntid, keyword: ":foo/boolean", boolean: true)
|
||||||
|
try builder.add(entid: aEntid, keyword: ":foo/instant", date: newDate)
|
||||||
|
let (inProgress, report) = try builder.transact()
|
||||||
|
try inProgress.transact(transaction: "[[:db/add \(aEntid) :foo/long 22]]")
|
||||||
|
try inProgress.commit()
|
||||||
|
...
|
||||||
|
} catch {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `commit` function will transact and commit the assertions that have been added to the `EntityBuilder`.
|
||||||
|
It will consume the `InProgress` used to perform the transact. It returns the `TxReport` generated by
|
||||||
|
the transact. After calling `commit`, a new transaction must be started by calling `Mentat.beginTransaction()`
|
||||||
|
in order to perform further actions.
|
||||||
|
|
||||||
|
```
|
||||||
|
let aEntid = txReport.entid(forTempId: "a")
|
||||||
|
let bEntid = txReport.entid(forTempId: "b")
|
||||||
|
do {
|
||||||
|
let builder = try mentat.entityBuilder(forEntid: aEntid)
|
||||||
|
try builder.add(entid: bEntid, keyword: ":foo/boolean", boolean: true)
|
||||||
|
try builder.add(entid: aEntid, keyword: ":foo/instant", date: newDate)
|
||||||
|
let report = try builder.commit()
|
||||||
|
...
|
||||||
|
} catch {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
class InProgressBuilder: OptionalRustObject {
|
||||||
|
|
||||||
|
/**
|
||||||
|
Asserts the value of attribute `keyword` to be the provided `value` for entity `entid`.
|
||||||
|
|
||||||
|
- Parameter entid: The `Entid` of the entity to be touched.
|
||||||
|
- Parameter keyword: The name of the attribute in the format `:namespace/name`.
|
||||||
|
- Parameter value: The value to be asserted
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if the attribute is not present in the schema or the attribute value type
|
||||||
|
is not `:db.type/long`.
|
||||||
|
*/
|
||||||
|
func add(entid: Entid, keyword: String, long value: Int64) throws {
|
||||||
|
try in_progress_builder_add_long(try self.validPointer(), entid, keyword, value).pointee.tryUnwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Asserts the value of attribute `keyword` to be the provided `value` for entity `entid`.
|
||||||
|
|
||||||
|
- Parameter entid: The `Entid` of the entity to be touched.
|
||||||
|
- Parameter keyword: The name of the attribute in the format `:namespace/name`.
|
||||||
|
- Parameter value: The value to be asserted
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if the attribute is not present in the schema or the attribute value type
|
||||||
|
is not `:db.type/ref`.
|
||||||
|
*/
|
||||||
|
func add(entid: Entid, keyword: String, reference value: Entid) throws {
|
||||||
|
try in_progress_builder_add_ref(try self.validPointer(), entid, keyword, value).pointee.tryUnwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Asserts the value of attribute `keyword` to be the provided `value` for entity `entid`.
|
||||||
|
|
||||||
|
- Parameter entid: The `Entid` of the entity to be touched.
|
||||||
|
- Parameter keyword: The name of the attribute in the format `:namespace/name`.
|
||||||
|
- Parameter value: The value to be asserted
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if the attribute is not present in the schema or the attribute value type
|
||||||
|
is not `:db.type/keyword`.
|
||||||
|
*/
|
||||||
|
func add(entid: Entid, keyword: String, keyword value: String) throws {
|
||||||
|
try in_progress_builder_add_keyword(try self.validPointer(), entid, keyword, value).pointee.tryUnwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Asserts the value of attribute `keyword` to be the provided `value` for entity `entid`.
|
||||||
|
|
||||||
|
- Parameter entid: The `Entid` of the entity to be touched.
|
||||||
|
- Parameter keyword: The name of the attribute in the format `:namespace/name`.
|
||||||
|
- Parameter value: The value to be asserted
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if the attribute is not present in the schema or the attribute value type
|
||||||
|
is not `:db.type/boolean`.
|
||||||
|
*/
|
||||||
|
func add(entid: Entid, keyword: String, boolean value: Bool) throws {
|
||||||
|
try in_progress_builder_add_boolean(try self.validPointer(), entid, keyword, value ? 1 : 0).pointee.tryUnwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Asserts the value of attribute `keyword` to be the provided `value` for entity `entid`.
|
||||||
|
|
||||||
|
- Parameter entid: The `Entid` of the entity to be touched.
|
||||||
|
- Parameter keyword: The name of the attribute in the format `:namespace/name`.
|
||||||
|
- Parameter value: The value to be asserted
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if the attribute is not present in the schema or the attribute value type
|
||||||
|
is not `:db.type/double`.
|
||||||
|
*/
|
||||||
|
func add(entid: Entid, keyword: String, double value: Double) throws {
|
||||||
|
try in_progress_builder_add_double(try self.validPointer(), entid, keyword, value).pointee.tryUnwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Asserts the value of attribute `keyword` to be the provided `value` for entity `entid`.
|
||||||
|
|
||||||
|
- Parameter entid: The `Entid` of the entity to be touched.
|
||||||
|
- Parameter keyword: The name of the attribute in the format `:namespace/name`.
|
||||||
|
- Parameter value: The value to be asserted
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if the attribute is not present in the schema or the attribute value type
|
||||||
|
is not `:db.type/instant`.
|
||||||
|
*/
|
||||||
|
func add(entid: Entid, keyword: String, date value: Date) throws {
|
||||||
|
try in_progress_builder_add_timestamp(try self.validPointer(), entid, keyword, value.toMicroseconds()).pointee.tryUnwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Asserts the value of attribute `keyword` to be the provided `value` for entity `entid`.
|
||||||
|
|
||||||
|
- Parameter entid: The `Entid` of the entity to be touched.
|
||||||
|
- Parameter keyword: The name of the attribute in the format `:namespace/name`.
|
||||||
|
- Parameter value: The value to be asserted
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if the attribute is not present in the schema or the attribute value type
|
||||||
|
is not `:db.type/string`.
|
||||||
|
*/
|
||||||
|
func add(entid: Entid, keyword: String, string value: String) throws {
|
||||||
|
try in_progress_builder_add_string(try self.validPointer(), entid, keyword, value).pointee.tryUnwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Asserts the value of attribute `keyword` to be the provided `value` for entity `entid`.
|
||||||
|
|
||||||
|
- Parameter entid: The `Entid` of the entity to be touched.
|
||||||
|
- Parameter keyword: The name of the attribute in the format `:namespace/name`.
|
||||||
|
- Parameter value: The value to be asserted
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if the attribute is not present in the schema or the attribute value type
|
||||||
|
is not `:db.type/uuid`.
|
||||||
|
*/
|
||||||
|
func add(entid: Entid, keyword: String, uuid value: UUID) throws {
|
||||||
|
var rawUuid = value.uuid
|
||||||
|
let _ = try withUnsafePointer(to: &rawUuid) { uuidPtr in
|
||||||
|
try in_progress_builder_add_uuid(try self.validPointer(), entid, keyword, uuidPtr).pointee.tryUnwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Retracts the value of attribute `keyword` from the provided `value` for entity `entid`.
|
||||||
|
|
||||||
|
- Parameter entid: The `Entid` of the entity to be touched.
|
||||||
|
- Parameter keyword: The name of the attribute in the format `:namespace/name`.
|
||||||
|
- Parameter value: The value to be retracted
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if the attribute is not present in the schema or the attribute value type
|
||||||
|
is not `:db.type/long`.
|
||||||
|
*/
|
||||||
|
func retract(entid: Entid, keyword: String, long value: Int64) throws {
|
||||||
|
try in_progress_builder_retract_long(try self.validPointer(), entid, keyword, value).pointee.tryUnwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Retracts the value of attribute `keyword` from the provided `value` for entity `entid`.
|
||||||
|
|
||||||
|
- Parameter entid: The `Entid` of the entity to be touched.
|
||||||
|
- Parameter keyword: The name of the attribute in the format `:namespace/name`.
|
||||||
|
- Parameter value: The value to be retracted
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if the attribute is not present in the schema or the attribute value type
|
||||||
|
is not `:db.type/ref`.
|
||||||
|
*/
|
||||||
|
func retract(entid: Entid, keyword: String, reference value: Entid) throws {
|
||||||
|
try in_progress_builder_retract_ref(try self.validPointer(), entid, keyword, value).pointee.tryUnwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Retracts the value of attribute `keyword` from the provided `value` for entity `entid`.
|
||||||
|
|
||||||
|
- Parameter entid: The `Entid` of the entity to be touched.
|
||||||
|
- Parameter keyword: The name of the attribute in the format `:namespace/name`.
|
||||||
|
- Parameter value: The value to be retracted
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if the attribute is not present in the schema or the attribute value type
|
||||||
|
is not `:db.type/keyword`.
|
||||||
|
*/
|
||||||
|
func retract(entid: Entid, keyword: String, keyword value: String) throws {
|
||||||
|
try in_progress_builder_retract_keyword(try self.validPointer(), entid, keyword, value).pointee.tryUnwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Retracts the value of attribute `keyword` from the provided `value` for entity `entid`.
|
||||||
|
|
||||||
|
- Parameter entid: The `Entid` of the entity to be touched.
|
||||||
|
- Parameter keyword: The name of the attribute in the format `:namespace/name`.
|
||||||
|
- Parameter value: The value to be retracted
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if the attribute is not present in the schema or the attribute value type
|
||||||
|
is not `:db.type/boolean`.
|
||||||
|
*/
|
||||||
|
func retract(entid: Entid, keyword: String, boolean value: Bool) throws {
|
||||||
|
try in_progress_builder_retract_boolean(try self.validPointer(), entid, keyword, value ? 1 : 0).pointee.tryUnwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Retracts the value of attribute `keyword` from the provided `value` for entity `entid`.
|
||||||
|
|
||||||
|
- Parameter entid: The `Entid` of the entity to be touched.
|
||||||
|
- Parameter keyword: The name of the attribute in the format `:namespace/name`.
|
||||||
|
- Parameter value: The value to be retracted
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if the attribute is not present in the schema or the attribute value type
|
||||||
|
is not `:db.type/double`.
|
||||||
|
*/
|
||||||
|
func retract(entid: Entid, keyword: String, double value: Double) throws {
|
||||||
|
try in_progress_builder_retract_double(try self.validPointer(), entid, keyword, value).pointee.tryUnwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Retracts the value of attribute `keyword` from the provided `value` for entity `entid`.
|
||||||
|
|
||||||
|
- Parameter entid: The `Entid` of the entity to be touched.
|
||||||
|
- Parameter keyword: The name of the attribute in the format `:namespace/name`.
|
||||||
|
- Parameter value: The value to be retracted
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if the attribute is not present in the schema or the attribute value type
|
||||||
|
is not `:db.type/instant`.
|
||||||
|
*/
|
||||||
|
func retract(entid: Entid, keyword: String, date value: Date) throws {
|
||||||
|
try in_progress_builder_retract_timestamp(try self.validPointer(), entid, keyword, value.toMicroseconds()).pointee.tryUnwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Retracts the value of attribute `keyword` from the provided `value` for entity `entid`.
|
||||||
|
|
||||||
|
- Parameter entid: The `Entid` of the entity to be touched.
|
||||||
|
- Parameter keyword: The name of the attribute in the format `:namespace/name`.
|
||||||
|
- Parameter value: The value to be retracted
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if the attribute is not present in the schema or the attribute value type
|
||||||
|
is not `:db.type/string`.
|
||||||
|
*/
|
||||||
|
func retract(entid: Entid, keyword: String, string value: String) throws {
|
||||||
|
try in_progress_builder_retract_string(try self.validPointer(), entid, keyword, value).pointee.tryUnwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Retracts the value of attribute `keyword` from the provided `value` for entity `entid`.
|
||||||
|
|
||||||
|
- Parameter entid: The `Entid` of the entity to be touched.
|
||||||
|
- Parameter keyword: The name of the attribute in the format `:namespace/name`.
|
||||||
|
- Parameter value: The value to be retracted
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if the attribute is not present in the schema or the attribute value type
|
||||||
|
is not `:db.type/uuid`.
|
||||||
|
*/
|
||||||
|
func retract(entid: Entid, keyword: String, uuid value: UUID) throws {
|
||||||
|
var rawUuid = value.uuid
|
||||||
|
let _ = try withUnsafePointer(to: &rawUuid) { uuidPtr in
|
||||||
|
try in_progress_builder_retract_uuid(try self.validPointer(), entid, keyword, uuidPtr).pointee.tryUnwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Transacts the added assertions. This consumes the pointer associated with this `InProgressBuilder`
|
||||||
|
such that no further assertions can be added after the `transact` has completed. To perform
|
||||||
|
further assertions, use the `InProgress` returned from this function.
|
||||||
|
|
||||||
|
This does not commit the transaction. In order to do so, `commit` can be called on the `InProgress` returned
|
||||||
|
from this function.
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if an error occured during the execution of the transact.
|
||||||
|
|
||||||
|
- Returns: The current `InProgress` and the `TxReport` generated by the transact.
|
||||||
|
*/
|
||||||
|
func transact() throws -> (InProgress, TxReport?) {
|
||||||
|
defer {
|
||||||
|
self.raw = nil
|
||||||
|
}
|
||||||
|
let result = in_progress_builder_transact(try self.validPointer()).pointee
|
||||||
|
let inProgress = InProgress(raw: result.inProgress)
|
||||||
|
guard let report = try result.result.pointee.tryUnwrap() else {
|
||||||
|
return (inProgress, nil)
|
||||||
|
}
|
||||||
|
return (inProgress, TxReport(raw: report))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Transacts the added assertions and commits. This consumes the pointer associated with this `InProgressBuilder`
|
||||||
|
and the associated `InProgress` such that no further assertions can be added after the `commit` has completed.
|
||||||
|
To perform further assertions, a new `InProgress` or `InProgressBuilder` should be created.
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the builder
|
||||||
|
has already been transacted or committed.
|
||||||
|
- Throws: `ResultError.error` if an error occured during the execution of the transact.
|
||||||
|
|
||||||
|
- Returns: The `TxReport` generated by the transact.
|
||||||
|
*/
|
||||||
|
func commit() throws -> TxReport {
|
||||||
|
defer {
|
||||||
|
self.raw = nil
|
||||||
|
}
|
||||||
|
return TxReport(raw: try in_progress_builder_commit(try self.validPointer()).pointee.unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
override func cleanup(pointer: OpaquePointer) {
|
||||||
|
in_progress_builder_destroy(pointer)
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,7 +13,7 @@ import Foundation
|
||||||
import MentatStore
|
import MentatStore
|
||||||
|
|
||||||
/**
|
/**
|
||||||
This class wraps a raw pointer than points to a Rust `TxReport` object.
|
This class wraps a raw pointer that points to a Rust `TxReport` object.
|
||||||
|
|
||||||
The `TxReport` contains information about a successful Mentat transaction.
|
The `TxReport` contains information about a successful Mentat transaction.
|
||||||
|
|
||||||
|
|
|
@ -60,6 +60,16 @@ struct Option {
|
||||||
};
|
};
|
||||||
typedef struct Option Option;
|
typedef struct Option Option;
|
||||||
|
|
||||||
|
/*
|
||||||
|
A mapping of the InProgressTransactResult repr(C) Rust object.
|
||||||
|
The memory for this is managed by Swift.
|
||||||
|
*/
|
||||||
|
struct InProgressTransactResult {
|
||||||
|
struct InProgress*_Nonnull inProgress;
|
||||||
|
struct Result*_Nonnull result;
|
||||||
|
};
|
||||||
|
typedef struct InProgressTransactResult InProgressTransactResult;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
A Mapping for the ValueType Rust object.
|
A Mapping for the ValueType Rust object.
|
||||||
*/
|
*/
|
||||||
|
@ -100,13 +110,72 @@ void typed_value_list_destroy(struct QueryResultRow* _Nullable obj);
|
||||||
void typed_value_list_iter_destroy(struct QueryRowIterator* _Nullable obj);
|
void typed_value_list_iter_destroy(struct QueryRowIterator* _Nullable obj);
|
||||||
void typed_value_result_set_destroy(struct QueryResultRows* _Nullable obj);
|
void typed_value_result_set_destroy(struct QueryResultRows* _Nullable obj);
|
||||||
void typed_value_result_set_iter_destroy(struct QueryRowsIterator* _Nullable obj);
|
void typed_value_result_set_iter_destroy(struct QueryRowsIterator* _Nullable obj);
|
||||||
|
void in_progress_destroy(struct InProgress* _Nullable obj);
|
||||||
|
void in_progress_builder_destroy(struct InProgressBuilder* _Nullable obj);
|
||||||
|
void entity_builder_destroy(struct EntityBuilder* _Nullable obj);
|
||||||
|
|
||||||
|
// caching
|
||||||
|
struct Result*_Nonnull store_cache_attribute_forward(struct Store*_Nonnull store, const char* _Nonnull attribute);
|
||||||
|
struct Result*_Nonnull store_cache_attribute_reverse(struct Store*_Nonnull store, const char* _Nonnull attribute);
|
||||||
|
struct Result*_Nonnull store_cache_attribute_bi_directional(struct Store*_Nonnull store, const char* _Nonnull attribute);
|
||||||
|
|
||||||
// transact
|
// transact
|
||||||
struct Result*_Nonnull store_transact(struct Store*_Nonnull store, const char* _Nonnull transaction);
|
struct Result*_Nonnull store_transact(struct Store*_Nonnull store, const char* _Nonnull transaction);
|
||||||
const int64_t* _Nullable tx_report_entity_for_temp_id(const struct TxReport* _Nonnull report, const char* _Nonnull tempid);
|
const int64_t* _Nullable tx_report_entity_for_temp_id(const struct TxReport* _Nonnull report, const char* _Nonnull tempid);
|
||||||
int64_t tx_report_get_entid(const struct TxReport* _Nonnull report);
|
int64_t tx_report_get_entid(const struct TxReport* _Nonnull report);
|
||||||
int64_t tx_report_get_tx_instant(const struct TxReport* _Nonnull report);
|
int64_t tx_report_get_tx_instant(const struct TxReport* _Nonnull report);
|
||||||
|
struct Result*_Nonnull store_begin_transaction(struct Store*_Nonnull store);
|
||||||
|
|
||||||
|
// in progress
|
||||||
|
struct Result*_Nonnull in_progress_transact(struct InProgress*_Nonnull in_progress, const char* _Nonnull transaction);
|
||||||
|
struct Result*_Nonnull in_progress_commit(struct InProgress*_Nonnull in_progress);
|
||||||
|
struct Result*_Nonnull in_progress_rollback(struct InProgress*_Nonnull in_progress);
|
||||||
|
|
||||||
|
// in_progress entity building
|
||||||
|
struct Result*_Nonnull store_in_progress_builder(struct Store*_Nonnull store);
|
||||||
|
struct InProgressBuilder*_Nonnull in_progress_builder(struct InProgress*_Nonnull in_progress);
|
||||||
|
struct EntityBuilder*_Nonnull in_progress_entity_builder_from_temp_id(struct InProgress*_Nonnull in_progress, const char*_Nonnull temp_id);
|
||||||
|
struct EntityBuilder*_Nonnull in_progress_entity_builder_from_entid(struct InProgress*_Nonnull in_progress, const int64_t entid);
|
||||||
|
struct Result*_Nonnull in_progress_builder_add_string(struct InProgressBuilder*_Nonnull builder, const int64_t entid, const char*_Nonnull kw, const char*_Nonnull value);
|
||||||
|
struct Result*_Nonnull in_progress_builder_add_long(struct InProgressBuilder*_Nonnull builder, const int64_t entid, const char*_Nonnull kw, const int64_t value);
|
||||||
|
struct Result*_Nonnull in_progress_builder_add_ref(struct InProgressBuilder*_Nonnull builder, const int64_t entid, const char*_Nonnull kw, const int64_t value);
|
||||||
|
struct Result*_Nonnull in_progress_builder_add_keyword(struct InProgressBuilder*_Nonnull builder, const int64_t entid, const char*_Nonnull kw, const char*_Nonnull value);
|
||||||
|
struct Result*_Nonnull in_progress_builder_add_timestamp(struct InProgressBuilder*_Nonnull builder, const int64_t entid, const char*_Nonnull kw, const int64_t value);
|
||||||
|
struct Result*_Nonnull in_progress_builder_add_boolean(struct InProgressBuilder*_Nonnull builder, const int64_t entid, const char*_Nonnull kw, const int32_t value);
|
||||||
|
struct Result*_Nonnull in_progress_builder_add_double(struct InProgressBuilder*_Nonnull builder, const int64_t entid, const char*_Nonnull kw, const double value);
|
||||||
|
struct Result*_Nonnull in_progress_builder_add_uuid(struct InProgressBuilder*_Nonnull builder, const int64_t entid, const char*_Nonnull kw, const uuid_t* _Nonnull value);
|
||||||
|
struct Result*_Nonnull in_progress_builder_retract_string(struct InProgressBuilder*_Nonnull builder, const int64_t entid, const char*_Nonnull kw, const char*_Nonnull value);
|
||||||
|
struct Result*_Nonnull in_progress_builder_retract_long(struct InProgressBuilder*_Nonnull builder, const int64_t entid, const char*_Nonnull kw, const int64_t value);
|
||||||
|
struct Result*_Nonnull in_progress_builder_retract_ref(struct InProgressBuilder*_Nonnull builder, const int64_t entid, const char*_Nonnull kw, const int64_t value);
|
||||||
|
struct Result*_Nonnull in_progress_builder_retract_keyword(struct InProgressBuilder*_Nonnull builder, const int64_t entid, const char*_Nonnull kw, const char*_Nonnull value);
|
||||||
|
struct Result*_Nonnull in_progress_builder_retract_timestamp(struct InProgressBuilder*_Nonnull builder, const int64_t entid, const char*_Nonnull kw, const int64_t value);
|
||||||
|
struct Result*_Nonnull in_progress_builder_retract_boolean(struct InProgressBuilder*_Nonnull builder, const int64_t entid, const char*_Nonnull kw, const int32_t value);
|
||||||
|
struct Result*_Nonnull in_progress_builder_retract_double(struct InProgressBuilder*_Nonnull builder, const int64_t entid, const char*_Nonnull kw, const double value);
|
||||||
|
struct Result*_Nonnull in_progress_builder_retract_uuid(struct InProgressBuilder*_Nonnull builder, const int64_t entid, const char*_Nonnull kw, const uuid_t* _Nonnull value);
|
||||||
|
struct InProgressTransactResult*_Nonnull in_progress_builder_transact(struct InProgressBuilder*_Nonnull builder);
|
||||||
|
struct Result*_Nonnull in_progress_builder_commit(struct InProgressBuilder*_Nonnull builder);
|
||||||
|
|
||||||
|
// entity building
|
||||||
|
struct Result*_Nonnull store_entity_builder_from_temp_id(struct Store*_Nonnull store, const char*_Nonnull temp_id);
|
||||||
|
struct Result*_Nonnull store_entity_builder_from_entid(struct Store*_Nonnull store, const int64_t entid);
|
||||||
|
struct Result*_Nonnull entity_builder_add_string(struct EntityBuilder*_Nonnull builder, const char*_Nonnull kw, const char*_Nonnull value);
|
||||||
|
struct Result*_Nonnull entity_builder_add_long(struct EntityBuilder*_Nonnull builder, const char*_Nonnull kw, const int64_t value);
|
||||||
|
struct Result*_Nonnull entity_builder_add_ref(struct EntityBuilder*_Nonnull builder, const char*_Nonnull kw, const int64_t value);
|
||||||
|
struct Result*_Nonnull entity_builder_add_keyword(struct EntityBuilder*_Nonnull builder, const char*_Nonnull kw, const char*_Nonnull value);
|
||||||
|
struct Result*_Nonnull entity_builder_add_boolean(struct EntityBuilder*_Nonnull builder, const char*_Nonnull kw, const int32_t value);
|
||||||
|
struct Result*_Nonnull entity_builder_add_double(struct EntityBuilder*_Nonnull builder, const char*_Nonnull kw, const double value);
|
||||||
|
struct Result*_Nonnull entity_builder_add_timestamp(struct EntityBuilder*_Nonnull builder, const char*_Nonnull kw, const int64_t value);
|
||||||
|
struct Result*_Nonnull entity_builder_add_uuid(struct EntityBuilder*_Nonnull builder, const char*_Nonnull kw, const uuid_t* _Nonnull value);
|
||||||
|
struct Result*_Nonnull entity_builder_retract_string(struct EntityBuilder*_Nonnull builder, const char*_Nonnull kw, const char*_Nonnull value);
|
||||||
|
struct Result*_Nonnull entity_builder_retract_long(struct EntityBuilder*_Nonnull builder, const char*_Nonnull kw, const int64_t value);
|
||||||
|
struct Result*_Nonnull entity_builder_retract_ref(struct EntityBuilder*_Nonnull builder, const char*_Nonnull kw, const int64_t value);
|
||||||
|
struct Result*_Nonnull entity_builder_retract_keyword(struct EntityBuilder*_Nonnull builder, const char*_Nonnull kw, const char*_Nonnull value);
|
||||||
|
struct Result*_Nonnull entity_builder_retract_boolean(struct EntityBuilder*_Nonnull builder, const char*_Nonnull kw, const int32_t value);
|
||||||
|
struct Result*_Nonnull entity_builder_retract_double(struct EntityBuilder*_Nonnull builder, const char*_Nonnull kw, const double value);
|
||||||
|
struct Result*_Nonnull entity_builder_retract_timestamp(struct EntityBuilder*_Nonnull builder, const char*_Nonnull kw, const int64_t value);
|
||||||
|
struct Result*_Nonnull entity_builder_retract_uuid(struct EntityBuilder*_Nonnull builder, const char*_Nonnull kw, const uuid_t* _Nonnull value);
|
||||||
|
struct InProgressTransactResult*_Nonnull entity_builder_transact(struct InProgressBuilder*_Nonnull builder);
|
||||||
|
struct Result*_Nonnull entity_builder_commit(struct EntityBuilder*_Nonnull builder);
|
||||||
|
|
||||||
// Sync
|
// Sync
|
||||||
struct Result*_Nonnull store_sync(struct Store*_Nonnull store, const char* _Nonnull user_uuid, const char* _Nonnull server_uri);
|
struct Result*_Nonnull store_sync(struct Store*_Nonnull store, const char* _Nonnull user_uuid, const char* _Nonnull server_uri);
|
||||||
|
|
|
@ -120,7 +120,8 @@ class MentatTests: XCTestCase {
|
||||||
[:db/add "u" :db/cardinality :db.cardinality/one]
|
[:db/add "u" :db/cardinality :db.cardinality/one]
|
||||||
]
|
]
|
||||||
"""
|
"""
|
||||||
let report = try mentat.transact(transaction: schema)
|
let transaction = try mentat.beginTransaction();
|
||||||
|
let report = try transaction.transact(transaction: schema)
|
||||||
let stringEntid = report.entid(forTempId: "s")!
|
let stringEntid = report.entid(forTempId: "s")!
|
||||||
|
|
||||||
let data = """
|
let data = """
|
||||||
|
@ -142,7 +143,8 @@ class MentatTests: XCTestCase {
|
||||||
[:db/add "b" :foo/uuid #uuid "4cb3f828-752d-497a-90c9-b1fd516d5644"]
|
[:db/add "b" :foo/uuid #uuid "4cb3f828-752d-497a-90c9-b1fd516d5644"]
|
||||||
]
|
]
|
||||||
"""
|
"""
|
||||||
let dataReport = try mentat.transact(transaction: data)
|
let dataReport = try transaction.transact(transaction: data)
|
||||||
|
try transaction.commit();
|
||||||
return (report, dataReport)
|
return (report, dataReport)
|
||||||
} catch {
|
} catch {
|
||||||
assertionFailure(error.localizedDescription)
|
assertionFailure(error.localizedDescription)
|
||||||
|
@ -760,6 +762,483 @@ class MentatTests: XCTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func test3InProgressTransact() {
|
||||||
|
let mentat = Mentat()
|
||||||
|
let (_, report) = self.populateWithTypesSchema(mentat: mentat)
|
||||||
|
XCTAssertNotNil(report)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testInProgressRollback() {
|
||||||
|
let mentat = Mentat()
|
||||||
|
let (_, report) = self.populateWithTypesSchema(mentat: mentat)
|
||||||
|
XCTAssertNotNil(report)
|
||||||
|
let aEntid = report!.entid(forTempId: "a")!
|
||||||
|
|
||||||
|
let preLongValue = try! mentat.value(forAttribute: ":foo/long", ofEntity: aEntid)
|
||||||
|
XCTAssertEqual(25, preLongValue?.asLong())
|
||||||
|
|
||||||
|
let inProgress = try! mentat.beginTransaction()
|
||||||
|
XCTAssertNoThrow(try inProgress.transact(transaction: "[[:db/add \(aEntid) :foo/long 22]]"))
|
||||||
|
XCTAssertNoThrow(try inProgress.rollback())
|
||||||
|
|
||||||
|
let postLongValue = try! mentat.value(forAttribute: ":foo/long", ofEntity: aEntid)
|
||||||
|
XCTAssertEqual(25, postLongValue?.asLong())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func testInProgressEntityBuilder() {
|
||||||
|
let mentat = Mentat()
|
||||||
|
let (schemaReport, dataReport) = self.populateWithTypesSchema(mentat: mentat)
|
||||||
|
let bEntid = dataReport!.entid(forTempId: "b")!
|
||||||
|
let longEntid = schemaReport!.entid(forTempId: "l")!
|
||||||
|
let stringEntid = schemaReport!.entid(forTempId: "s")!
|
||||||
|
// test that the values are as expected
|
||||||
|
let query = """
|
||||||
|
[:find [?b ?i ?u ?l ?d ?s ?k ?r]
|
||||||
|
:in ?e
|
||||||
|
:where [?e :foo/boolean ?b]
|
||||||
|
[?e :foo/instant ?i]
|
||||||
|
[?e :foo/uuid ?u]
|
||||||
|
[?e :foo/long ?l]
|
||||||
|
[?e :foo/double ?d]
|
||||||
|
[?e :foo/string ?s]
|
||||||
|
[?e :foo/keyword ?k]
|
||||||
|
[?e :foo/ref ?r]]
|
||||||
|
"""
|
||||||
|
let formatter = DateFormatter()
|
||||||
|
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
|
||||||
|
XCTAssertNoThrow(try mentat.query(query: query).bind(varName: "?e", toReference: bEntid).runTuple { (result) in
|
||||||
|
XCTAssertNotNil(result)
|
||||||
|
XCTAssertEqual(false, result?.asBool(index: 0))
|
||||||
|
|
||||||
|
let previousDate = formatter.date(from: "2018-01-01T11:00:00+00:00")
|
||||||
|
XCTAssertEqual(previousDate, result?.asDate(index: 1))
|
||||||
|
|
||||||
|
let previousUuid = UUID(uuidString: "4cb3f828-752d-497a-90c9-b1fd516d5644")!
|
||||||
|
XCTAssertEqual(previousUuid, result?.asUUID(index: 2))
|
||||||
|
|
||||||
|
XCTAssertEqual(50, result?.asLong(index: 3))
|
||||||
|
XCTAssertEqual(22.46, result?.asDouble(index: 4))
|
||||||
|
XCTAssertEqual("Silence is worse; all truths that are kept silent become poisonous.", result?.asString(index: 5))
|
||||||
|
XCTAssertEqual(":foo/string", result?.asKeyword(index: 6))
|
||||||
|
XCTAssertEqual(stringEntid, result?.asEntid(index: 7))
|
||||||
|
})
|
||||||
|
|
||||||
|
let builder = try! mentat.entityBuilder()
|
||||||
|
XCTAssertNoThrow(try builder.add(entid: bEntid, keyword: ":foo/boolean", boolean: true))
|
||||||
|
let newDate = Date()
|
||||||
|
XCTAssertNoThrow(try builder.add(entid: bEntid, keyword: ":foo/instant", date: newDate))
|
||||||
|
let newUUID = UUID()
|
||||||
|
XCTAssertNoThrow(try builder.add(entid: bEntid, keyword: ":foo/uuid", uuid: newUUID))
|
||||||
|
XCTAssertNoThrow(try builder.add(entid: bEntid, keyword: ":foo/long", long: 75))
|
||||||
|
XCTAssertNoThrow(try builder.add(entid: bEntid, keyword: ":foo/double", double: 81.3))
|
||||||
|
XCTAssertNoThrow(try builder.add(entid: bEntid, keyword: ":foo/string", string: "Become who you are!"))
|
||||||
|
XCTAssertNoThrow(try builder.add(entid: bEntid, keyword: ":foo/keyword", keyword: ":foo/long"))
|
||||||
|
XCTAssertNoThrow(try builder.add(entid: bEntid, keyword: ":foo/ref", reference: longEntid))
|
||||||
|
XCTAssertNoThrow(try builder.commit())
|
||||||
|
|
||||||
|
// test that the values have changed
|
||||||
|
XCTAssertNoThrow(try mentat.query(query: query).bind(varName: "?e", toReference: bEntid).runTuple { (result) in
|
||||||
|
XCTAssertNotNil(result)
|
||||||
|
XCTAssertEqual(true, result?.asBool(index: 0))
|
||||||
|
XCTAssertEqual(formatter.string(from: newDate), formatter.string(from: result!.asDate(index: 1)))
|
||||||
|
XCTAssertEqual(newUUID, result?.asUUID(index: 2))
|
||||||
|
XCTAssertEqual(75, result?.asLong(index: 3))
|
||||||
|
XCTAssertEqual(81.3, result?.asDouble(index: 4))
|
||||||
|
XCTAssertEqual("Become who you are!", result?.asString(index: 5))
|
||||||
|
XCTAssertEqual(":foo/long", result?.asKeyword(index: 6))
|
||||||
|
XCTAssertEqual(longEntid, result?.asEntid(index: 7))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testEntityBuilderForEntid() {
|
||||||
|
let mentat = Mentat()
|
||||||
|
let (schemaReport, dataReport) = self.populateWithTypesSchema(mentat: mentat)
|
||||||
|
let bEntid = dataReport!.entid(forTempId: "b")!
|
||||||
|
let longEntid = schemaReport!.entid(forTempId: "l")!
|
||||||
|
let stringEntid = schemaReport!.entid(forTempId: "s")!
|
||||||
|
// test that the values are as expected
|
||||||
|
let query = """
|
||||||
|
[:find [?b ?i ?u ?l ?d ?s ?k ?r]
|
||||||
|
:in ?e
|
||||||
|
:where [?e :foo/boolean ?b]
|
||||||
|
[?e :foo/instant ?i]
|
||||||
|
[?e :foo/uuid ?u]
|
||||||
|
[?e :foo/long ?l]
|
||||||
|
[?e :foo/double ?d]
|
||||||
|
[?e :foo/string ?s]
|
||||||
|
[?e :foo/keyword ?k]
|
||||||
|
[?e :foo/ref ?r]]
|
||||||
|
"""
|
||||||
|
let formatter = DateFormatter()
|
||||||
|
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
|
||||||
|
XCTAssertNoThrow(try mentat.query(query: query).bind(varName: "?e", toReference: bEntid).runTuple { (result) in
|
||||||
|
XCTAssertNotNil(result)
|
||||||
|
XCTAssertEqual(false, result?.asBool(index: 0))
|
||||||
|
|
||||||
|
let previousDate = formatter.date(from: "2018-01-01T11:00:00+00:00")
|
||||||
|
XCTAssertEqual(previousDate, result?.asDate(index: 1))
|
||||||
|
|
||||||
|
let previousUuid = UUID(uuidString: "4cb3f828-752d-497a-90c9-b1fd516d5644")!
|
||||||
|
XCTAssertEqual(previousUuid, result?.asUUID(index: 2))
|
||||||
|
|
||||||
|
XCTAssertEqual(50, result?.asLong(index: 3))
|
||||||
|
XCTAssertEqual(22.46, result?.asDouble(index: 4))
|
||||||
|
XCTAssertEqual("Silence is worse; all truths that are kept silent become poisonous.", result?.asString(index: 5))
|
||||||
|
XCTAssertEqual(":foo/string", result?.asKeyword(index: 6))
|
||||||
|
XCTAssertEqual(stringEntid, result?.asEntid(index: 7))
|
||||||
|
})
|
||||||
|
|
||||||
|
let builder = try! mentat.entityBuilder(forEntid: bEntid)
|
||||||
|
XCTAssertNoThrow(try builder.add(keyword: ":foo/boolean", boolean: true))
|
||||||
|
let newDate = Date()
|
||||||
|
XCTAssertNoThrow(try builder.add(keyword: ":foo/instant", date: newDate))
|
||||||
|
let newUUID = UUID()
|
||||||
|
XCTAssertNoThrow(try builder.add(keyword: ":foo/uuid", uuid: newUUID))
|
||||||
|
XCTAssertNoThrow(try builder.add(keyword: ":foo/long", long: 75))
|
||||||
|
XCTAssertNoThrow(try builder.add(keyword: ":foo/double", double: 81.3))
|
||||||
|
XCTAssertNoThrow(try builder.add(keyword: ":foo/string", string: "Become who you are!"))
|
||||||
|
XCTAssertNoThrow(try builder.add(keyword: ":foo/keyword", keyword: ":foo/long"))
|
||||||
|
XCTAssertNoThrow(try builder.add(keyword: ":foo/ref", reference: longEntid))
|
||||||
|
XCTAssertNoThrow(try builder.commit())
|
||||||
|
|
||||||
|
// test that the values have changed
|
||||||
|
XCTAssertNoThrow(try mentat.query(query: query).bind(varName: "?e", toReference: bEntid).runTuple { (result) in
|
||||||
|
XCTAssertNotNil(result)
|
||||||
|
XCTAssertEqual(true, result?.asBool(index: 0))
|
||||||
|
XCTAssertEqual(formatter.string(from: newDate), formatter.string(from: result!.asDate(index: 1)))
|
||||||
|
XCTAssertEqual(newUUID, result?.asUUID(index: 2))
|
||||||
|
XCTAssertEqual(75, result?.asLong(index: 3))
|
||||||
|
XCTAssertEqual(81.3, result?.asDouble(index: 4))
|
||||||
|
XCTAssertEqual("Become who you are!", result?.asString(index: 5))
|
||||||
|
XCTAssertEqual(":foo/long", result?.asKeyword(index: 6))
|
||||||
|
XCTAssertEqual(longEntid, result?.asEntid(index: 7))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testEntityBuilderForTempid() {
|
||||||
|
let mentat = Mentat()
|
||||||
|
let (schemaReport, _) = self.populateWithTypesSchema(mentat: mentat)
|
||||||
|
let longEntid = schemaReport!.entid(forTempId: "l")!
|
||||||
|
// test that the values are as expected
|
||||||
|
let query = """
|
||||||
|
[:find [?b ?i ?u ?l ?d ?s ?k ?r]
|
||||||
|
:in ?e
|
||||||
|
:where [?e :foo/boolean ?b]
|
||||||
|
[?e :foo/instant ?i]
|
||||||
|
[?e :foo/uuid ?u]
|
||||||
|
[?e :foo/long ?l]
|
||||||
|
[?e :foo/double ?d]
|
||||||
|
[?e :foo/string ?s]
|
||||||
|
[?e :foo/keyword ?k]
|
||||||
|
[?e :foo/ref ?r]]
|
||||||
|
"""
|
||||||
|
let formatter = DateFormatter()
|
||||||
|
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
|
||||||
|
|
||||||
|
let builder = try! mentat.entityBuilder(forTempId: "c")
|
||||||
|
XCTAssertNoThrow(try builder.add(keyword: ":foo/boolean", boolean: true))
|
||||||
|
let newDate = Date()
|
||||||
|
XCTAssertNoThrow(try builder.add(keyword: ":foo/instant", date: newDate))
|
||||||
|
let newUUID = UUID()
|
||||||
|
XCTAssertNoThrow(try builder.add(keyword: ":foo/uuid", uuid: newUUID))
|
||||||
|
XCTAssertNoThrow(try builder.add(keyword: ":foo/long", long: 75))
|
||||||
|
XCTAssertNoThrow(try builder.add(keyword: ":foo/double", double: 81.3))
|
||||||
|
XCTAssertNoThrow(try builder.add(keyword: ":foo/string", string: "Become who you are!"))
|
||||||
|
XCTAssertNoThrow(try builder.add(keyword: ":foo/keyword", keyword: ":foo/long"))
|
||||||
|
XCTAssertNoThrow(try builder.add(keyword: ":foo/ref", reference: longEntid))
|
||||||
|
let report = try! builder.commit()
|
||||||
|
let cEntid = report.entid(forTempId: "c")!
|
||||||
|
// test that the values have changed
|
||||||
|
XCTAssertNoThrow(try mentat.query(query: query).bind(varName: "?e", toReference: cEntid).runTuple { (result) in
|
||||||
|
XCTAssertNotNil(result)
|
||||||
|
XCTAssertEqual(true, result?.asBool(index: 0))
|
||||||
|
XCTAssertEqual(formatter.string(from: newDate), formatter.string(from: result!.asDate(index: 1)))
|
||||||
|
XCTAssertEqual(newUUID, result?.asUUID(index: 2))
|
||||||
|
XCTAssertEqual(75, result?.asLong(index: 3))
|
||||||
|
XCTAssertEqual(81.3, result?.asDouble(index: 4))
|
||||||
|
XCTAssertEqual("Become who you are!", result?.asString(index: 5))
|
||||||
|
XCTAssertEqual(":foo/long", result?.asKeyword(index: 6))
|
||||||
|
XCTAssertEqual(longEntid, result?.asEntid(index: 7))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testInProgressBuilderTransact() {
|
||||||
|
let mentat = Mentat()
|
||||||
|
let (schemaReport, dataReport) = self.populateWithTypesSchema(mentat: mentat)
|
||||||
|
let aEntid = dataReport!.entid(forTempId: "a")!
|
||||||
|
let bEntid = dataReport!.entid(forTempId: "b")!
|
||||||
|
let longEntid = schemaReport!.entid(forTempId: "l")!
|
||||||
|
// test that the values are as expected
|
||||||
|
let query = """
|
||||||
|
[:find [?b ?i ?u ?l ?d ?s ?k ?r]
|
||||||
|
:in ?e
|
||||||
|
:where [?e :foo/boolean ?b]
|
||||||
|
[?e :foo/instant ?i]
|
||||||
|
[?e :foo/uuid ?u]
|
||||||
|
[?e :foo/long ?l]
|
||||||
|
[?e :foo/double ?d]
|
||||||
|
[?e :foo/string ?s]
|
||||||
|
[?e :foo/keyword ?k]
|
||||||
|
[?e :foo/ref ?r]]
|
||||||
|
"""
|
||||||
|
let formatter = DateFormatter()
|
||||||
|
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
|
||||||
|
|
||||||
|
let builder = try! mentat.entityBuilder()
|
||||||
|
XCTAssertNoThrow(try builder.add(entid: bEntid, keyword: ":foo/boolean", boolean: true))
|
||||||
|
let newDate = Date()
|
||||||
|
XCTAssertNoThrow(try builder.add(entid: bEntid, keyword: ":foo/instant", date: newDate))
|
||||||
|
let newUUID = UUID()
|
||||||
|
XCTAssertNoThrow(try builder.add(entid: bEntid, keyword: ":foo/uuid", uuid: newUUID))
|
||||||
|
XCTAssertNoThrow(try builder.add(entid: bEntid, keyword: ":foo/long", long: 75))
|
||||||
|
XCTAssertNoThrow(try builder.add(entid: bEntid, keyword: ":foo/double", double: 81.3))
|
||||||
|
XCTAssertNoThrow(try builder.add(entid: bEntid, keyword: ":foo/string", string: "Become who you are!"))
|
||||||
|
XCTAssertNoThrow(try builder.add(entid: bEntid, keyword: ":foo/keyword", keyword: ":foo/long"))
|
||||||
|
XCTAssertNoThrow(try builder.add(entid: bEntid, keyword: ":foo/ref", reference: longEntid))
|
||||||
|
let (inProgress, report) = try! builder.transact()
|
||||||
|
XCTAssertNotNil(inProgress)
|
||||||
|
XCTAssertNotNil(report)
|
||||||
|
XCTAssertNoThrow(try inProgress.transact(transaction: "[[:db/add \(aEntid) :foo/long 22]]"))
|
||||||
|
XCTAssertNoThrow(try inProgress.commit())
|
||||||
|
|
||||||
|
// test that the values have changed
|
||||||
|
XCTAssertNoThrow(try mentat.query(query: query).bind(varName: "?e", toReference: bEntid).runTuple { (result) in
|
||||||
|
XCTAssertNotNil(result)
|
||||||
|
XCTAssertEqual(true, result?.asBool(index: 0))
|
||||||
|
XCTAssertEqual(formatter.string(from: newDate), formatter.string(from: result!.asDate(index: 1)))
|
||||||
|
XCTAssertEqual(newUUID, result?.asUUID(index: 2))
|
||||||
|
XCTAssertEqual(75, result?.asLong(index: 3))
|
||||||
|
XCTAssertEqual(81.3, result?.asDouble(index: 4))
|
||||||
|
XCTAssertEqual("Become who you are!", result?.asString(index: 5))
|
||||||
|
XCTAssertEqual(":foo/long", result?.asKeyword(index: 6))
|
||||||
|
XCTAssertEqual(longEntid, result?.asEntid(index: 7))
|
||||||
|
})
|
||||||
|
|
||||||
|
let longValue = try! mentat.value(forAttribute: ":foo/long", ofEntity: aEntid)
|
||||||
|
XCTAssertEqual(22, longValue?.asLong())
|
||||||
|
}
|
||||||
|
|
||||||
|
func testEntityBuilderTransact() {
|
||||||
|
let mentat = Mentat()
|
||||||
|
let (schemaReport, dataReport) = self.populateWithTypesSchema(mentat: mentat)
|
||||||
|
let aEntid = dataReport!.entid(forTempId: "a")!
|
||||||
|
let bEntid = dataReport!.entid(forTempId: "b")!
|
||||||
|
let longEntid = schemaReport!.entid(forTempId: "l")!
|
||||||
|
// test that the values are as expected
|
||||||
|
let query = """
|
||||||
|
[:find [?b ?i ?u ?l ?d ?s ?k ?r]
|
||||||
|
:in ?e
|
||||||
|
:where [?e :foo/boolean ?b]
|
||||||
|
[?e :foo/instant ?i]
|
||||||
|
[?e :foo/uuid ?u]
|
||||||
|
[?e :foo/long ?l]
|
||||||
|
[?e :foo/double ?d]
|
||||||
|
[?e :foo/string ?s]
|
||||||
|
[?e :foo/keyword ?k]
|
||||||
|
[?e :foo/ref ?r]]
|
||||||
|
"""
|
||||||
|
let formatter = DateFormatter()
|
||||||
|
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
|
||||||
|
|
||||||
|
let builder = try! mentat.entityBuilder(forEntid: bEntid)
|
||||||
|
XCTAssertNoThrow(try builder.add(keyword: ":foo/boolean", boolean: true))
|
||||||
|
let newDate = Date()
|
||||||
|
XCTAssertNoThrow(try builder.add(keyword: ":foo/instant", date: newDate))
|
||||||
|
let newUUID = UUID()
|
||||||
|
XCTAssertNoThrow(try builder.add(keyword: ":foo/uuid", uuid: newUUID))
|
||||||
|
XCTAssertNoThrow(try builder.add(keyword: ":foo/long", long: 75))
|
||||||
|
XCTAssertNoThrow(try builder.add(keyword: ":foo/double", double: 81.3))
|
||||||
|
XCTAssertNoThrow(try builder.add(keyword: ":foo/string", string: "Become who you are!"))
|
||||||
|
XCTAssertNoThrow(try builder.add(keyword: ":foo/keyword", keyword: ":foo/long"))
|
||||||
|
XCTAssertNoThrow(try builder.add(keyword: ":foo/ref", reference: longEntid))
|
||||||
|
let (inProgress, report) = try! builder.transact()
|
||||||
|
XCTAssertNotNil(inProgress)
|
||||||
|
XCTAssertNotNil(report)
|
||||||
|
XCTAssertNoThrow(try inProgress.transact(transaction: "[[:db/add \(aEntid) :foo/long 22]]"))
|
||||||
|
XCTAssertNoThrow(try inProgress.commit())
|
||||||
|
|
||||||
|
// test that the values have changed
|
||||||
|
XCTAssertNoThrow(try mentat.query(query: query).bind(varName: "?e", toReference: bEntid).runTuple { (result) in
|
||||||
|
XCTAssertNotNil(result)
|
||||||
|
XCTAssertEqual(true, result?.asBool(index: 0))
|
||||||
|
XCTAssertEqual(formatter.string(from: newDate), formatter.string(from: result!.asDate(index: 1)))
|
||||||
|
XCTAssertEqual(newUUID, result?.asUUID(index: 2))
|
||||||
|
XCTAssertEqual(75, result?.asLong(index: 3))
|
||||||
|
XCTAssertEqual(81.3, result?.asDouble(index: 4))
|
||||||
|
XCTAssertEqual("Become who you are!", result?.asString(index: 5))
|
||||||
|
XCTAssertEqual(":foo/long", result?.asKeyword(index: 6))
|
||||||
|
XCTAssertEqual(longEntid, result?.asEntid(index: 7))
|
||||||
|
})
|
||||||
|
|
||||||
|
let longValue = try! mentat.value(forAttribute: ":foo/long", ofEntity: aEntid)
|
||||||
|
XCTAssertEqual(22, longValue?.asLong())
|
||||||
|
}
|
||||||
|
|
||||||
|
func testEntityBuilderRetract() {
|
||||||
|
let mentat = Mentat()
|
||||||
|
let (schemaReport, dataReport) = self.populateWithTypesSchema(mentat: mentat)
|
||||||
|
let bEntid = dataReport!.entid(forTempId: "b")!
|
||||||
|
let stringEntid = schemaReport!.entid(forTempId: "s")!
|
||||||
|
// test that the values are as expected
|
||||||
|
let query = """
|
||||||
|
[:find [?b ?i ?u ?l ?d ?s ?k ?r]
|
||||||
|
:in ?e
|
||||||
|
:where [?e :foo/boolean ?b]
|
||||||
|
[?e :foo/instant ?i]
|
||||||
|
[?e :foo/uuid ?u]
|
||||||
|
[?e :foo/long ?l]
|
||||||
|
[?e :foo/double ?d]
|
||||||
|
[?e :foo/string ?s]
|
||||||
|
[?e :foo/keyword ?k]
|
||||||
|
[?e :foo/ref ?r]]
|
||||||
|
"""
|
||||||
|
let formatter = DateFormatter()
|
||||||
|
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
|
||||||
|
let previousDate = formatter.date(from: "2018-01-01T11:00:00+00:00")!
|
||||||
|
let previousUuid = UUID(uuidString: "4cb3f828-752d-497a-90c9-b1fd516d5644")!
|
||||||
|
XCTAssertNoThrow(try mentat.query(query: query).bind(varName: "?e", toReference: bEntid).runTuple { (result) in
|
||||||
|
XCTAssertNotNil(result)
|
||||||
|
XCTAssertEqual(false, result?.asBool(index: 0))
|
||||||
|
XCTAssertEqual(previousDate, result?.asDate(index: 1))
|
||||||
|
XCTAssertEqual(previousUuid, result?.asUUID(index: 2))
|
||||||
|
XCTAssertEqual(50, result?.asLong(index: 3))
|
||||||
|
XCTAssertEqual(22.46, result?.asDouble(index: 4))
|
||||||
|
XCTAssertEqual("Silence is worse; all truths that are kept silent become poisonous.", result?.asString(index: 5))
|
||||||
|
XCTAssertEqual(":foo/string", result?.asKeyword(index: 6))
|
||||||
|
XCTAssertEqual(stringEntid, result?.asEntid(index: 7))
|
||||||
|
})
|
||||||
|
|
||||||
|
let builder = try! mentat.entityBuilder(forEntid: bEntid)
|
||||||
|
XCTAssertNoThrow(try builder.retract(keyword: ":foo/boolean", boolean: false))
|
||||||
|
XCTAssertNoThrow(try builder.retract(keyword: ":foo/instant", date: previousDate))
|
||||||
|
XCTAssertNoThrow(try builder.retract(keyword: ":foo/uuid", uuid: previousUuid))
|
||||||
|
XCTAssertNoThrow(try builder.retract(keyword: ":foo/long", long: 50))
|
||||||
|
XCTAssertNoThrow(try builder.retract(keyword: ":foo/double", double: 22.46))
|
||||||
|
XCTAssertNoThrow(try builder.retract(keyword: ":foo/string", string: "Silence is worse; all truths that are kept silent become poisonous."))
|
||||||
|
XCTAssertNoThrow(try builder.retract(keyword: ":foo/keyword", keyword: ":foo/string"))
|
||||||
|
XCTAssertNoThrow(try builder.retract(keyword: ":foo/ref", reference: stringEntid))
|
||||||
|
XCTAssertNoThrow(try builder.commit())
|
||||||
|
|
||||||
|
XCTAssertNoThrow(try mentat.query(query: query).bind(varName: "?e", toReference: bEntid).runTuple { (result) in
|
||||||
|
XCTAssertNil(result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testInProgressEntityBuilderRetract() {
|
||||||
|
let mentat = Mentat()
|
||||||
|
let (schemaReport, dataReport) = self.populateWithTypesSchema(mentat: mentat)
|
||||||
|
let bEntid = dataReport!.entid(forTempId: "b")!
|
||||||
|
let stringEntid = schemaReport!.entid(forTempId: "s")!
|
||||||
|
// test that the values are as expected
|
||||||
|
let query = """
|
||||||
|
[:find [?b ?i ?u ?l ?d ?s ?k ?r]
|
||||||
|
:in ?e
|
||||||
|
:where [?e :foo/boolean ?b]
|
||||||
|
[?e :foo/instant ?i]
|
||||||
|
[?e :foo/uuid ?u]
|
||||||
|
[?e :foo/long ?l]
|
||||||
|
[?e :foo/double ?d]
|
||||||
|
[?e :foo/string ?s]
|
||||||
|
[?e :foo/keyword ?k]
|
||||||
|
[?e :foo/ref ?r]]
|
||||||
|
"""
|
||||||
|
let formatter = DateFormatter()
|
||||||
|
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
|
||||||
|
let previousDate = formatter.date(from: "2018-01-01T11:00:00+00:00")!
|
||||||
|
let previousUuid = UUID(uuidString: "4cb3f828-752d-497a-90c9-b1fd516d5644")!
|
||||||
|
XCTAssertNoThrow(try mentat.query(query: query).bind(varName: "?e", toReference: bEntid).runTuple { (result) in
|
||||||
|
XCTAssertNotNil(result)
|
||||||
|
XCTAssertEqual(false, result?.asBool(index: 0))
|
||||||
|
XCTAssertEqual(previousDate, result?.asDate(index: 1))
|
||||||
|
XCTAssertEqual(previousUuid, result?.asUUID(index: 2))
|
||||||
|
XCTAssertEqual(50, result?.asLong(index: 3))
|
||||||
|
XCTAssertEqual(22.46, result?.asDouble(index: 4))
|
||||||
|
XCTAssertEqual("Silence is worse; all truths that are kept silent become poisonous.", result?.asString(index: 5))
|
||||||
|
XCTAssertEqual(":foo/string", result?.asKeyword(index: 6))
|
||||||
|
XCTAssertEqual(stringEntid, result?.asEntid(index: 7))
|
||||||
|
})
|
||||||
|
|
||||||
|
let builder = try! mentat.entityBuilder()
|
||||||
|
XCTAssertNoThrow(try builder.retract(entid: bEntid, keyword: ":foo/boolean", boolean: false))
|
||||||
|
XCTAssertNoThrow(try builder.retract(entid: bEntid, keyword: ":foo/instant", date: previousDate))
|
||||||
|
XCTAssertNoThrow(try builder.retract(entid: bEntid, keyword: ":foo/uuid", uuid: previousUuid))
|
||||||
|
XCTAssertNoThrow(try builder.retract(entid: bEntid, keyword: ":foo/long", long: 50))
|
||||||
|
XCTAssertNoThrow(try builder.retract(entid: bEntid, keyword: ":foo/double", double: 22.46))
|
||||||
|
XCTAssertNoThrow(try builder.retract(entid: bEntid, keyword: ":foo/string", string: "Silence is worse; all truths that are kept silent become poisonous."))
|
||||||
|
XCTAssertNoThrow(try builder.retract(entid: bEntid, keyword: ":foo/keyword", keyword: ":foo/string"))
|
||||||
|
XCTAssertNoThrow(try builder.retract(entid: bEntid, keyword: ":foo/ref", reference: stringEntid))
|
||||||
|
XCTAssertNoThrow(try builder.commit())
|
||||||
|
|
||||||
|
XCTAssertNoThrow(try mentat.query(query: query).bind(varName: "?e", toReference: bEntid).runTuple { (result) in
|
||||||
|
XCTAssertNil(result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCaching() {
|
||||||
|
let query = """
|
||||||
|
[:find ?district :where
|
||||||
|
[?neighborhood :neighborhood/name \"Beacon Hill\"]
|
||||||
|
[?neighborhood :neighborhood/district ?d]
|
||||||
|
[?d :district/name ?district]]
|
||||||
|
"""
|
||||||
|
|
||||||
|
let mentat = openAndInitializeCitiesStore()
|
||||||
|
|
||||||
|
struct QueryTimer {
|
||||||
|
private var _start: UInt64
|
||||||
|
private var _end: UInt64
|
||||||
|
|
||||||
|
init() {
|
||||||
|
self._start = 0
|
||||||
|
self._end = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
private func currentTimeNanos() -> UInt64 {
|
||||||
|
var info = mach_timebase_info()
|
||||||
|
guard mach_timebase_info(&info) == KERN_SUCCESS else { return 0 }
|
||||||
|
let currentTime = mach_absolute_time()
|
||||||
|
return currentTime * UInt64(info.numer) / UInt64(info.denom)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
mutating func start() {
|
||||||
|
self._start = self.currentTimeNanos()
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func end() {
|
||||||
|
self._end = self.currentTimeNanos()
|
||||||
|
}
|
||||||
|
|
||||||
|
func duration() -> UInt64 {
|
||||||
|
return self._end - self._start
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var uncachedTimer = QueryTimer()
|
||||||
|
uncachedTimer.start()
|
||||||
|
|
||||||
|
XCTAssertNoThrow(try mentat.query(query: query).run { (result) in
|
||||||
|
uncachedTimer.end()
|
||||||
|
XCTAssertNotNil(result)
|
||||||
|
})
|
||||||
|
|
||||||
|
XCTAssertNoThrow(try mentat.cache(attribute: ":neighborhood/name", direction: CacheDirection.reverse))
|
||||||
|
XCTAssertNoThrow(try mentat.cache(attribute: ":neighborhood/district", direction: CacheDirection.forward))
|
||||||
|
|
||||||
|
var cachedTimer = QueryTimer()
|
||||||
|
cachedTimer.start()
|
||||||
|
|
||||||
|
XCTAssertNoThrow(try mentat.query(query: query).run { (result) in
|
||||||
|
cachedTimer.end()
|
||||||
|
XCTAssertNotNil(result)
|
||||||
|
})
|
||||||
|
|
||||||
|
let timingDifference = uncachedTimer.duration() - cachedTimer.duration()
|
||||||
|
print("Cached query is \(timingDifference) nanoseconds faster than the uncached query")
|
||||||
|
|
||||||
|
XCTAssertLessThan(cachedTimer.duration(), uncachedTimer.duration())
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Add tests for transaction observation
|
// TODO: Add tests for transaction observation
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue