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
820
ffi/src/lib.rs
820
ffi/src/lib.rs
|
@ -87,11 +87,12 @@ use std::vec;
|
|||
|
||||
pub use mentat::{
|
||||
Binding,
|
||||
CacheDirection,
|
||||
Entid,
|
||||
FindSpec,
|
||||
HasSchema,
|
||||
InProgress,
|
||||
KnownEntid,
|
||||
Keyword,
|
||||
Queryable,
|
||||
QueryBuilder,
|
||||
QueryInputs,
|
||||
|
@ -126,6 +127,7 @@ pub use utils::strings::{
|
|||
|
||||
pub use utils::log;
|
||||
|
||||
// type aliases for iterator types.
|
||||
pub type BindingIterator = vec::IntoIter<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.
|
||||
/// 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
|
||||
|
@ -247,17 +256,737 @@ pub extern "C" fn store_open(uri: *const c_char) -> *mut Store {
|
|||
|
||||
// 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
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
// 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]
|
||||
pub unsafe extern "C" fn store_transact(store: *mut Store, transaction: *const c_char) -> *mut ExternResult {
|
||||
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()))
|
||||
}
|
||||
|
||||
/// Fetches the `tx_id` for the given [TxReport](mentat::TxReport).
|
||||
/// Fetches the `tx_id` for the given [TxReport](mentat::TxReport)`.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn tx_report_get_entid(tx_report: *mut TxReport) -> c_longlong {
|
||||
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.
|
||||
///
|
||||
|
@ -501,7 +1261,7 @@ fn unwrap_conversion<T>(value: Option<T>, expected_type: ValueType) -> T {
|
|||
///
|
||||
/// If the [ValueType](mentat::ValueType) of the [Binding](mentat::Binding) is not [ValueType::Long](mentat::ValueType::Long).
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn typed_value_into_long(typed_value: *mut Binding) -> c_longlong {
|
||||
pub unsafe extern "C" fn typed_value_into_long(typed_value: *mut Binding) -> c_longlong {
|
||||
let typed_value = Box::from_raw(typed_value);
|
||||
unwrap_conversion(typed_value.into_long(), ValueType::Long)
|
||||
}
|
||||
|
@ -510,10 +1270,11 @@ pub unsafe extern "C" fn typed_value_into_long(typed_value: *mut Binding) -> c_l
|
|||
///
|
||||
/// # 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]
|
||||
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);
|
||||
println!("typed value as entid {:?}", typed_value);
|
||||
unwrap_conversion(typed_value.into_entid(), ValueType::Ref)
|
||||
}
|
||||
|
||||
|
@ -521,9 +1282,9 @@ pub unsafe extern "C" fn typed_value_into_entid(typed_value: *mut Binding) -> En
|
|||
///
|
||||
/// # 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]
|
||||
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);
|
||||
unwrap_conversion(typed_value.into_kw_c_string(), ValueType::Keyword) as *const c_char
|
||||
}
|
||||
|
@ -588,7 +1349,7 @@ pub unsafe extern "C" fn typed_value_into_uuid(typed_value: *mut Binding) -> *m
|
|||
|
||||
/// Returns the [ValueType](mentat::ValueType) of this [Binding](mentat::Binding).
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn typed_value_value_type(typed_value: *mut Binding) -> ValueType {
|
||||
pub unsafe extern "C" fn typed_value_value_type(typed_value: *mut Binding) -> ValueType {
|
||||
let typed_value = &*typed_value;
|
||||
typed_value.value_type().unwrap_or_else(|| panic!("Binding is not Scalar and has no ValueType"))
|
||||
}
|
||||
|
@ -602,12 +1363,12 @@ pub unsafe extern "C" fn typed_value_value_type(typed_value: *mut Binding) -> Va
|
|||
/// A destructor `typed_value_result_set_destroy` is provided for releasing the memory for this
|
||||
/// pointer type.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn row_at_index(rows: *mut RelResult<Binding>, index: c_int) -> *mut Vec<Binding> {
|
||||
pub unsafe extern "C" fn row_at_index(rows: *mut RelResult<Binding>, index: c_int) -> *mut Vec<Binding> {
|
||||
let result = &*rows;
|
||||
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
|
||||
///
|
||||
|
@ -615,7 +1376,7 @@ pub unsafe extern "C" fn row_at_index(rows: *mut RelResult<Binding>, index: c_in
|
|||
/// A destructor `typed_value_result_set_iter_destroy` is provided for releasing the memory for this
|
||||
/// pointer type.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn typed_value_result_set_into_iter(rows: *mut RelResult<Binding>) -> *mut BindingListIterator {
|
||||
pub unsafe extern "C" fn typed_value_result_set_into_iter(rows: *mut RelResult<Binding>) -> *mut BindingListIterator {
|
||||
let result = &*rows;
|
||||
let rows = result.rows();
|
||||
Box::into_raw(Box::new(rows))
|
||||
|
@ -630,7 +1391,7 @@ pub unsafe extern "C" fn typed_value_result_set_into_iter(rows: *mut RelResult<B
|
|||
/// A destructor `typed_value_list_destroy` is provided for releasing the memory for this
|
||||
/// pointer type.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn typed_value_result_set_iter_next(iter: *mut BindingListIterator) -> *mut Vec<Binding> {
|
||||
pub unsafe extern "C" fn typed_value_result_set_iter_next(iter: *mut BindingListIterator) -> *mut Vec<Binding> {
|
||||
let iter = &mut *iter;
|
||||
iter.next().map_or(std::ptr::null_mut(), |v| Box::into_raw(Box::new(v.to_vec())))
|
||||
}
|
||||
|
@ -643,7 +1404,7 @@ pub unsafe extern "C" fn typed_value_result_set_iter_next(iter: *mut BindingList
|
|||
/// A destructor `typed_value_list_iter_destroy` is provided for releasing the memory for this
|
||||
/// pointer type.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn typed_value_list_into_iter(values: *mut Vec<Binding>) -> *mut BindingIterator {
|
||||
pub unsafe extern "C" fn typed_value_list_into_iter(values: *mut Vec<Binding>) -> *mut BindingIterator {
|
||||
let result = Box::from_raw(values);
|
||||
Box::into_raw(Box::new(result.into_iter()))
|
||||
}
|
||||
|
@ -657,7 +1418,7 @@ pub unsafe extern "C" fn typed_value_list_into_iter(values: *mut Vec<Binding>) -
|
|||
/// A destructor `typed_value_destroy` is provided for releasing the memory for this
|
||||
/// pointer type.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn typed_value_list_iter_next(iter: *mut BindingIterator) -> *mut Binding {
|
||||
pub unsafe extern "C" fn typed_value_list_iter_next(iter: *mut BindingIterator) -> *mut Binding {
|
||||
let iter = &mut *iter;
|
||||
iter.next().map_or(std::ptr::null_mut(), |v| Box::into_raw(Box::new(v)))
|
||||
}
|
||||
|
@ -905,6 +1666,18 @@ pub unsafe extern "C" fn changelist_entry_at(tx_report: *mut TransactionChange,
|
|||
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.
|
||||
#[no_mangle]
|
||||
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).
|
||||
define_destructor!(typed_value_destroy, Binding);
|
||||
|
||||
/// Destructor for releasing the memory of [Vec<Binding>][mentat::Binding].
|
||||
define_destructor!(typed_value_list_destroy, Vec<Binding>);
|
||||
|
||||
/// Destructor for releasing the memory of [BindingIterator](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>);
|
||||
|
||||
/// 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);
|
||||
|
||||
/// Destructor for releasing the memory of [InProgress](mentat::InProgress).
|
||||
define_destructor!(in_progress_destroy, InProgress);
|
||||
|
|
|
@ -29,7 +29,7 @@ pub mod strings {
|
|||
}
|
||||
|
||||
pub fn kw_from_string(keyword_string: &'static str) -> Keyword {
|
||||
// TODO: validate. The input might not be a keyword!
|
||||
// TODO: validate. The input might not be a keyword!
|
||||
let attr_name = keyword_string.trim_left_matches(":");
|
||||
let parts: Vec<&str> = attr_name.split("/").collect();
|
||||
Keyword::namespaced(parts[0], parts[1])
|
||||
|
|
|
@ -14,6 +14,7 @@ import android.content.Context;
|
|||
import android.content.res.AssetManager;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.util.Log;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
@ -22,7 +23,6 @@ import java.io.BufferedReader;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
|
@ -40,6 +40,33 @@ import static org.junit.Assert.*;
|
|||
@RunWith(AndroidJUnit4.class)
|
||||
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;
|
||||
|
||||
@Test
|
||||
|
@ -95,7 +122,8 @@ public class FFIIntegrationTest {
|
|||
return this.mentat;
|
||||
}
|
||||
|
||||
public TxReport populateWithTypesSchema(Mentat mentat) {
|
||||
public DBSetupResult populateWithTypesSchema(Mentat mentat) {
|
||||
InProgress transaction = mentat.beginTransaction();
|
||||
String schema = "[\n" +
|
||||
" [:db/add \"b\" :db/ident :foo/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/cardinality :db.cardinality/one]\n" +
|
||||
" ]";
|
||||
TxReport report = mentat.transact(schema);
|
||||
TxReport report = transaction.transact(schema);
|
||||
Long stringEntid = report.getEntidForTempId("s");
|
||||
|
||||
String data = "[\n" +
|
||||
|
@ -135,13 +163,16 @@ public class FFIIntegrationTest {
|
|||
" [:db/add \"a\" :foo/uuid #uuid \"550e8400-e29b-41d4-a716-446655440000\"]\n" +
|
||||
" [:db/add \"b\" :foo/boolean false]\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/instant #inst \"2018-01-01T11:00:00.000Z\"]\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/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
|
||||
|
@ -168,7 +199,7 @@ public class FFIIntegrationTest {
|
|||
Mentat mentat = openAndInitializeCitiesStore();
|
||||
String query = "[:find ?n . :in ?name :where [(fulltext $ :community/name ?name) [[?e ?n]]]]";
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).bindString("?name", "Wallingford").runScalar(new ScalarResultHandler() {
|
||||
mentat.query(query).bind("?name", "Wallingford").run(new ScalarResultHandler() {
|
||||
@Override
|
||||
public void handleValue(TypedValue value) {
|
||||
assertNotNull(value);
|
||||
|
@ -187,7 +218,7 @@ public class FFIIntegrationTest {
|
|||
Mentat mentat = openAndInitializeCitiesStore();
|
||||
String query = "[:find [?when ...] :where [_ :db/txInstant ?when] :order (asc ?when)]";
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).runColl(new CollResultHandler() {
|
||||
mentat.query(query).run(new CollResultHandler() {
|
||||
@Override
|
||||
public void handleList(CollResult list) {
|
||||
assertNotNull(list);
|
||||
|
@ -208,7 +239,7 @@ public class FFIIntegrationTest {
|
|||
Mentat mentat = openAndInitializeCitiesStore();
|
||||
String query = "[:find [?when ...] :where [_ :db/txInstant ?when] :order (asc ?when)]";
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).runColl(new CollResultHandler() {
|
||||
mentat.query(query).run(new CollResultHandler() {
|
||||
@Override
|
||||
public void handleList(CollResult list) {
|
||||
assertNotNull(list);
|
||||
|
@ -234,7 +265,7 @@ public class FFIIntegrationTest {
|
|||
" [?c :community/type :community.type/website]\n" +
|
||||
" [(fulltext $ :community/category \"food\") [[?c ?cat]]]]";
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).runTuple(new TupleResultHandler() {
|
||||
mentat.query(query).run(new TupleResultHandler() {
|
||||
@Override
|
||||
public void handleRow(TupleResult row) {
|
||||
assertNotNull(row);
|
||||
|
@ -333,11 +364,11 @@ public class FFIIntegrationTest {
|
|||
@Test
|
||||
public void bindingLongValueSucceeds() throws InterruptedException {
|
||||
Mentat mentat = new Mentat();
|
||||
TxReport report = this.populateWithTypesSchema(mentat);
|
||||
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||
final Long aEntid = report.getEntidForTempId("a");
|
||||
String query = "[:find ?e . :in ?long :where [?e :foo/long ?long]]";
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).bindLong("?long", 25).runScalar(new ScalarResultHandler() {
|
||||
mentat.query(query).bind("?long", 25).run(new ScalarResultHandler() {
|
||||
@Override
|
||||
public void handleValue(TypedValue value) {
|
||||
assertNotNull(value);
|
||||
|
@ -354,12 +385,12 @@ public class FFIIntegrationTest {
|
|||
@Test
|
||||
public void bindingRefValueSucceeds() throws InterruptedException {
|
||||
Mentat mentat = new Mentat();
|
||||
TxReport report = this.populateWithTypesSchema(mentat);
|
||||
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||
long stringEntid = mentat.entIdForAttribute(":foo/string");
|
||||
final Long bEntid = report.getEntidForTempId("b");
|
||||
String query = "[:find ?e . :in ?ref :where [?e :foo/ref ?ref]]";
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).bindEntidReference("?ref", stringEntid).runScalar(new ScalarResultHandler() {
|
||||
mentat.query(query).bindEntidReference("?ref", stringEntid).run(new ScalarResultHandler() {
|
||||
@Override
|
||||
public void handleValue(TypedValue value) {
|
||||
assertNotNull(value);
|
||||
|
@ -376,12 +407,12 @@ public class FFIIntegrationTest {
|
|||
@Test
|
||||
public void bindingRefKwValueSucceeds() throws InterruptedException {
|
||||
Mentat mentat = new Mentat();
|
||||
TxReport report = this.populateWithTypesSchema(mentat);
|
||||
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||
String refKeyword = ":foo/string";
|
||||
final Long bEntid = report.getEntidForTempId("b");
|
||||
String query = "[:find ?e . :in ?ref :where [?e :foo/ref ?ref]]";
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).bindKeywordReference("?ref", refKeyword).runScalar(new ScalarResultHandler() {
|
||||
mentat.query(query).bindKeywordReference("?ref", refKeyword).run(new ScalarResultHandler() {
|
||||
@Override
|
||||
public void handleValue(TypedValue value) {
|
||||
assertNotNull(value);
|
||||
|
@ -398,11 +429,11 @@ public class FFIIntegrationTest {
|
|||
@Test
|
||||
public void bindingKwValueSucceeds() throws InterruptedException {
|
||||
Mentat mentat = new Mentat();
|
||||
TxReport report = this.populateWithTypesSchema(mentat);
|
||||
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||
final Long aEntid = report.getEntidForTempId("a");
|
||||
String query = "[:find ?e . :in ?kw :where [?e :foo/keyword ?kw]]";
|
||||
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
|
||||
public void handleValue(TypedValue value) {
|
||||
assertNotNull(value);
|
||||
|
@ -419,13 +450,13 @@ public class FFIIntegrationTest {
|
|||
@Test
|
||||
public void bindingDateValueSucceeds() throws InterruptedException, ParseException {
|
||||
Mentat mentat = new Mentat();
|
||||
TxReport report = this.populateWithTypesSchema(mentat);
|
||||
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||
final Long aEntid = report.getEntidForTempId("a");
|
||||
|
||||
Date date = new Date(1523896758000L);
|
||||
String query = "[:find [?e ?d] :in ?now :where [?e :foo/instant ?d] [(< ?d ?now)]]";
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).bindDate("?now", date).runTuple(new TupleResultHandler() {
|
||||
mentat.query(query).bind("?now", date).run(new TupleResultHandler() {
|
||||
@Override
|
||||
public void handleRow(TupleResult row) {
|
||||
assertNotNull(row);
|
||||
|
@ -446,7 +477,7 @@ public class FFIIntegrationTest {
|
|||
Mentat mentat = this.openAndInitializeCitiesStore();
|
||||
String query = "[:find ?n . :in ?name :where [(fulltext $ :community/name ?name) [[?e ?n]]]]";
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).bindString("?name", "Wallingford").runScalar(new ScalarResultHandler() {
|
||||
mentat.query(query).bind("?name", "Wallingford").run(new ScalarResultHandler() {
|
||||
@Override
|
||||
public void handleValue(TypedValue value) {
|
||||
assertNotNull(value);
|
||||
|
@ -463,12 +494,12 @@ public class FFIIntegrationTest {
|
|||
@Test
|
||||
public void bindingUuidValueSucceeds() throws InterruptedException {
|
||||
Mentat mentat = new Mentat();
|
||||
TxReport report = this.populateWithTypesSchema(mentat);
|
||||
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||
final Long aEntid = report.getEntidForTempId("a");
|
||||
String query = "[:find ?e . :in ?uuid :where [?e :foo/uuid ?uuid]]";
|
||||
UUID uuid = UUID.fromString("550e8400-e29b-41d4-a716-446655440000");
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).bindUUID("?uuid", uuid).runScalar(new ScalarResultHandler() {
|
||||
mentat.query(query).bind("?uuid", uuid).run(new ScalarResultHandler() {
|
||||
@Override
|
||||
public void handleValue(TypedValue value) {
|
||||
assertNotNull(value);
|
||||
|
@ -485,11 +516,11 @@ public class FFIIntegrationTest {
|
|||
@Test
|
||||
public void bindingBooleanValueSucceeds() throws InterruptedException {
|
||||
Mentat mentat = new Mentat();
|
||||
TxReport report = this.populateWithTypesSchema(mentat);
|
||||
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||
final Long aEntid = report.getEntidForTempId("a");
|
||||
String query = "[:find ?e . :in ?bool :where [?e :foo/boolean ?bool]]";
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).bindBoolean("?bool", true).runScalar(new ScalarResultHandler() {
|
||||
mentat.query(query).bind("?bool", true).run(new ScalarResultHandler() {
|
||||
@Override
|
||||
public void handleValue(TypedValue value) {
|
||||
assertNotNull(value);
|
||||
|
@ -507,11 +538,11 @@ public class FFIIntegrationTest {
|
|||
@Test
|
||||
public void bindingDoubleValueSucceeds() throws InterruptedException {
|
||||
Mentat mentat = new Mentat();
|
||||
TxReport report = this.populateWithTypesSchema(mentat);
|
||||
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||
final Long aEntid = report.getEntidForTempId("a");
|
||||
String query = "[:find ?e . :in ?double :where [?e :foo/double ?double]]";
|
||||
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
|
||||
public void handleValue(TypedValue value) {
|
||||
assertNotNull(value);
|
||||
|
@ -528,11 +559,11 @@ public class FFIIntegrationTest {
|
|||
@Test
|
||||
public void typedValueConvertsToLong() throws InterruptedException {
|
||||
Mentat mentat = new Mentat();
|
||||
TxReport report = this.populateWithTypesSchema(mentat);
|
||||
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||
final Long aEntid = report.getEntidForTempId("a");
|
||||
String query = "[:find ?v . :in ?e :where [?e :foo/long ?v]]";
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).bindEntidReference("?e", aEntid).runScalar(new ScalarResultHandler() {
|
||||
mentat.query(query).bindEntidReference("?e", aEntid).run(new ScalarResultHandler() {
|
||||
@Override
|
||||
public void handleValue(TypedValue value) {
|
||||
assertNotNull(value);
|
||||
|
@ -550,11 +581,11 @@ public class FFIIntegrationTest {
|
|||
@Test
|
||||
public void typedValueConvertsToRef() throws InterruptedException {
|
||||
Mentat mentat = new Mentat();
|
||||
TxReport report = this.populateWithTypesSchema(mentat);
|
||||
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||
final Long aEntid = report.getEntidForTempId("a");
|
||||
String query = "[:find ?e . :where [?e :foo/long 25]]";
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).runScalar(new ScalarResultHandler() {
|
||||
mentat.query(query).run(new ScalarResultHandler() {
|
||||
@Override
|
||||
public void handleValue(TypedValue value) {
|
||||
assertNotNull(value);
|
||||
|
@ -572,11 +603,11 @@ public class FFIIntegrationTest {
|
|||
@Test
|
||||
public void typedValueConvertsToKeyword() throws InterruptedException {
|
||||
Mentat mentat = new Mentat();
|
||||
TxReport report = this.populateWithTypesSchema(mentat);
|
||||
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||
final Long aEntid = report.getEntidForTempId("a");
|
||||
String query = "[:find ?v . :in ?e :where [?e :foo/keyword ?v]]";
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).bindEntidReference("?e", aEntid).runScalar(new ScalarResultHandler() {
|
||||
mentat.query(query).bindEntidReference("?e", aEntid).run(new ScalarResultHandler() {
|
||||
@Override
|
||||
public void handleValue(TypedValue value) {
|
||||
assertNotNull(value);
|
||||
|
@ -594,11 +625,11 @@ public class FFIIntegrationTest {
|
|||
@Test
|
||||
public void typedValueConvertsToBoolean() throws InterruptedException {
|
||||
Mentat mentat = new Mentat();
|
||||
TxReport report = this.populateWithTypesSchema(mentat);
|
||||
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||
final Long aEntid = report.getEntidForTempId("a");
|
||||
String query = "[:find ?v . :in ?e :where [?e :foo/boolean ?v]]";
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).bindEntidReference("?e", aEntid).runScalar(new ScalarResultHandler() {
|
||||
mentat.query(query).bindEntidReference("?e", aEntid).run(new ScalarResultHandler() {
|
||||
@Override
|
||||
public void handleValue(TypedValue value) {
|
||||
assertNotNull(value);
|
||||
|
@ -616,11 +647,11 @@ public class FFIIntegrationTest {
|
|||
@Test
|
||||
public void typedValueConvertsToDouble() throws InterruptedException {
|
||||
Mentat mentat = new Mentat();
|
||||
TxReport report = this.populateWithTypesSchema(mentat);
|
||||
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||
final Long aEntid = report.getEntidForTempId("a");
|
||||
String query = "[:find ?v . :in ?e :where [?e :foo/double ?v]]";
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).bindEntidReference("?e", aEntid).runScalar(new ScalarResultHandler() {
|
||||
mentat.query(query).bindEntidReference("?e", aEntid).run(new ScalarResultHandler() {
|
||||
@Override
|
||||
public void handleValue(TypedValue value) {
|
||||
assertNotNull(value);
|
||||
|
@ -638,14 +669,14 @@ public class FFIIntegrationTest {
|
|||
@Test
|
||||
public void typedValueConvertsToDate() throws InterruptedException, ParseException {
|
||||
Mentat mentat = new Mentat();
|
||||
TxReport report = this.populateWithTypesSchema(mentat);
|
||||
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||
final Long aEntid = report.getEntidForTempId("a");
|
||||
String query = "[:find ?v . :in ?e :where [?e :foo/instant ?v]]";
|
||||
final Expectation expectation = new Expectation();
|
||||
DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ", Locale.ENGLISH);
|
||||
format.parse("2017-01-01T11:00:00+00:00");
|
||||
final Calendar expectedDate = format.getCalendar();
|
||||
mentat.query(query).bindEntidReference("?e", aEntid).runScalar(new ScalarResultHandler() {
|
||||
mentat.query(query).bindEntidReference("?e", aEntid).run(new ScalarResultHandler() {
|
||||
@Override
|
||||
public void handleValue(TypedValue value) {
|
||||
assertNotNull(value);
|
||||
|
@ -663,11 +694,11 @@ public class FFIIntegrationTest {
|
|||
@Test
|
||||
public void typedValueConvertsToString() throws InterruptedException {
|
||||
Mentat mentat = new Mentat();
|
||||
TxReport report = this.populateWithTypesSchema(mentat);
|
||||
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||
final Long aEntid = report.getEntidForTempId("a");
|
||||
String query = "[:find ?v . :in ?e :where [?e :foo/string ?v]]";
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).bindEntidReference("?e", aEntid).runScalar(new ScalarResultHandler() {
|
||||
mentat.query(query).bindEntidReference("?e", aEntid).run(new ScalarResultHandler() {
|
||||
@Override
|
||||
public void handleValue(TypedValue value) {
|
||||
assertNotNull(value);
|
||||
|
@ -685,12 +716,12 @@ public class FFIIntegrationTest {
|
|||
@Test
|
||||
public void typedValueConvertsToUUID() throws InterruptedException {
|
||||
Mentat mentat = new Mentat();
|
||||
TxReport report = this.populateWithTypesSchema(mentat);
|
||||
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||
final Long aEntid = report.getEntidForTempId("a");
|
||||
String query = "[:find ?v . :in ?e :where [?e :foo/uuid ?v]]";
|
||||
final UUID expectedUUID = UUID.fromString("550e8400-e29b-41d4-a716-446655440000");
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).bindEntidReference("?e", aEntid).runScalar(new ScalarResultHandler() {
|
||||
mentat.query(query).bindEntidReference("?e", aEntid).run(new ScalarResultHandler() {
|
||||
@Override
|
||||
public void handleValue(TypedValue value) {
|
||||
assertNotNull(value);
|
||||
|
@ -708,7 +739,7 @@ public class FFIIntegrationTest {
|
|||
@Test
|
||||
public void valueForAttributeOfEntitySucceeds() throws InterruptedException {
|
||||
Mentat mentat = new Mentat();
|
||||
TxReport report = this.populateWithTypesSchema(mentat);
|
||||
TxReport report = this.populateWithTypesSchema(mentat).dataReport;
|
||||
final Long aEntid = report.getEntidForTempId("a");
|
||||
TypedValue value = mentat.valueForAttributeOfEntity(":foo/long", aEntid);
|
||||
assertNotNull(value);
|
||||
|
@ -722,4 +753,569 @@ public class FFIIntegrationTest {
|
|||
long entid = mentat.entIdForAttribute(":foo/long");
|
||||
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_iter_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
|
||||
RustResult store_transact(Pointer store, String transaction);
|
||||
Pointer tx_report_entity_for_temp_id(Pointer report, String tempid);
|
||||
long tx_report_get_entid(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
|
||||
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_string(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);
|
||||
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; }
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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.
|
||||
* @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
|
||||
* 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 entid The `Entid` of the entity we want the value from.
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
public void close() {
|
||||
if (this.rawPointer != null) {
|
||||
|
|
|
@ -76,7 +76,7 @@ import java.util.UUID;
|
|||
* <pre>{@code
|
||||
* String query = "[: find ?a .\n" +
|
||||
* " : where ... ]";
|
||||
* mentat.query(query).runScalar(new ScalarResultHandler() {
|
||||
* mentat.query(query).run(new ScalarResultHandler() {
|
||||
* @Override
|
||||
* public void handleValue(TypedValue value) {
|
||||
* ...
|
||||
|
@ -88,7 +88,7 @@ import java.util.UUID;
|
|||
* <pre>{@code
|
||||
* String query = "[: find [?a ...]\n" +
|
||||
* " : where ... ]";
|
||||
* mentat.query(query).runColl(new ScalarResultHandler() {
|
||||
* mentat.query(query).run(new ScalarResultHandler() {
|
||||
* @Override
|
||||
* public void handleList(CollResult list) {
|
||||
* ...
|
||||
|
@ -100,7 +100,7 @@ import java.util.UUID;
|
|||
* <pre>{@code
|
||||
* String query = "[: find [?a ?b ?c]\n" +
|
||||
* " : where ... ]";
|
||||
* mentat.query(query).runTuple(new TupleResultHandler() {
|
||||
* mentat.query(query).run(new TupleResultHandler() {
|
||||
* @Override
|
||||
* public void handleRow(TupleResult row) {
|
||||
* ...
|
||||
|
@ -121,7 +121,7 @@ public class Query extends RustObject {
|
|||
* @param value The value to be bound
|
||||
* @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();
|
||||
JNA.INSTANCE.query_builder_bind_long(this.rawPointer, varName, value);
|
||||
return this;
|
||||
|
@ -173,7 +173,7 @@ public class Query extends RustObject {
|
|||
* @param value The value to be bound
|
||||
* @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();
|
||||
JNA.INSTANCE.query_builder_bind_boolean(this.rawPointer, varName, value ? 1 : 0);
|
||||
return this;
|
||||
|
@ -186,7 +186,7 @@ public class Query extends RustObject {
|
|||
* @param value The value to be bound
|
||||
* @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();
|
||||
JNA.INSTANCE.query_builder_bind_double(this.rawPointer, varName, value);
|
||||
return this;
|
||||
|
@ -199,7 +199,7 @@ public class Query extends RustObject {
|
|||
* @param value The value to be bound
|
||||
* @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();
|
||||
long timestamp = value.getTime() * 1000;
|
||||
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
|
||||
* @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();
|
||||
JNA.INSTANCE.query_builder_bind_string(this.rawPointer, varName, value);
|
||||
return this;
|
||||
|
@ -226,15 +226,9 @@ public class Query extends RustObject {
|
|||
* @param value The value to be bound
|
||||
* @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();
|
||||
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
|
||||
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);
|
||||
JNA.INSTANCE.query_builder_bind_uuid(this.rawPointer, varName, getPointerForUUID(value));
|
||||
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
|
||||
* @param handler the handler to call with the results of this query
|
||||
*/
|
||||
void runScalar(final ScalarResultHandler handler) {
|
||||
void run(final ScalarResultHandler handler) {
|
||||
this.validate();
|
||||
RustResult result = JNA.INSTANCE.query_builder_execute_scalar(rawPointer);
|
||||
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
|
||||
* @param handler the handler to call with the results of this query
|
||||
*/
|
||||
void runColl(final CollResultHandler handler) {
|
||||
void run(final CollResultHandler handler) {
|
||||
this.validate();
|
||||
RustResult result = JNA.INSTANCE.query_builder_execute_coll(rawPointer);
|
||||
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
|
||||
* @param handler the handler to call with the results of this query
|
||||
*/
|
||||
void runTuple(final TupleResultHandler handler) {
|
||||
void run(final TupleResultHandler handler) {
|
||||
this.validate();
|
||||
RustResult result = JNA.INSTANCE.query_builder_execute_tuple(rawPointer);
|
||||
rawPointer = null;
|
||||
|
|
|
@ -10,9 +10,12 @@
|
|||
|
||||
package com.mozilla.mentat;
|
||||
|
||||
import com.sun.jna.Memory;
|
||||
import com.sun.jna.Pointer;
|
||||
|
||||
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.
|
||||
|
@ -31,4 +34,22 @@ abstract class RustObject implements Closeable {
|
|||
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) {
|
||||
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) {
|
||||
this.validate();
|
||||
Pointer uuidPtr = 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);
|
||||
return getUUIDFromPointer(JNA.INSTANCE.value_at_index_into_uuid(this.rawPointer, index));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -137,12 +137,7 @@ public class TypedValue extends RustObject {
|
|||
*/
|
||||
public UUID asUUID() {
|
||||
if (!this.isConsumed()) {
|
||||
Pointer uuidPtr = 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.value = getUUIDFromPointer(JNA.INSTANCE.typed_value_into_uuid(this.rawPointer));
|
||||
this.rawPointer = null;
|
||||
}
|
||||
return (UUID)this.value;
|
||||
|
|
Binary file not shown.
|
@ -7,6 +7,9 @@
|
|||
objects = {
|
||||
|
||||
/* 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 */; };
|
||||
7BAE75A22089020E00895D37 /* libmentat_ffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7BEB7D23207BE2AF000369AD /* libmentat_ffi.a */; };
|
||||
7BAE75A42089022B00895D37 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 7BAE75A32089022B00895D37 /* libsqlite3.tbd */; };
|
||||
|
@ -39,6 +42,9 @@
|
|||
/* End PBXContainerItemProxy 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; };
|
||||
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; };
|
||||
|
@ -196,6 +202,9 @@
|
|||
7BEB7D26207BE5BB000369AD /* Transact */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7B64E44B209094510063909F /* EntityBuilder.swift */,
|
||||
7B64E44C209094520063909F /* InProgress.swift */,
|
||||
7B64E44A209094510063909F /* InProgressBuilder.swift */,
|
||||
7BEB7D2B207D03DA000369AD /* TxReport.swift */,
|
||||
);
|
||||
path = Transact;
|
||||
|
@ -321,8 +330,11 @@
|
|||
7BDB96CC207B7684009D0651 /* Errors.swift in Sources */,
|
||||
7BDB96B02077C38E009D0651 /* Mentat.swift in Sources */,
|
||||
7BDB96B72077C38E009D0651 /* TypedValue.swift in Sources */,
|
||||
7B64E44E209094520063909F /* EntityBuilder.swift in Sources */,
|
||||
7B64E44D209094520063909F /* InProgressBuilder.swift in Sources */,
|
||||
7BDB96B52077C38E009D0651 /* TupleResult.swift in Sources */,
|
||||
7B74483D208DF667006CFFB0 /* Result+Unwrap.swift in Sources */,
|
||||
7B64E44F209094520063909F /* InProgress.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
@ -37,14 +37,6 @@
|
|||
BlueprintName = "MentatTests"
|
||||
ReferencedContainer = "container:Mentat.xcodeproj">
|
||||
</BuildableReference>
|
||||
<SkippedTests>
|
||||
<Test
|
||||
Identifier = "MentatTests/testBindBoolean()">
|
||||
</Test>
|
||||
<Test
|
||||
Identifier = "MentatTests/testBindDate()">
|
||||
</Test>
|
||||
</SkippedTests>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
//
|
||||
/* Copyright 2018 Mozilla
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
|
|
|
@ -29,6 +29,12 @@ protocol Observable {
|
|||
func unregister(key: String)
|
||||
}
|
||||
|
||||
enum CacheDirection {
|
||||
case forward;
|
||||
case reverse;
|
||||
case both;
|
||||
}
|
||||
|
||||
/**
|
||||
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.
|
||||
|
@ -56,11 +62,36 @@ class Mentat: RustObject {
|
|||
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.
|
||||
- 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
|
||||
*/
|
||||
|
@ -69,8 +100,67 @@ class Mentat: RustObject {
|
|||
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.
|
||||
|
||||
- Parameter attribute: The string represeting the attribute whose `Entid` we are after.
|
||||
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
|
||||
|
||||
/**
|
||||
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.
|
||||
|
||||
|
|
|
@ -60,6 +60,16 @@ struct 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.
|
||||
*/
|
||||
|
@ -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_result_set_destroy(struct QueryResultRows* _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
|
||||
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);
|
||||
int64_t tx_report_get_entid(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
|
||||
struct Result*_Nonnull store_sync(struct Store*_Nonnull store, const char* _Nonnull user_uuid, const char* _Nonnull server_uri);
|
||||
|
|
|
@ -17,12 +17,12 @@ class MentatTests: XCTestCase {
|
|||
var citiesSchema: String?
|
||||
var seattleData: String?
|
||||
var store: Mentat?
|
||||
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
|
||||
override func tearDown() {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
super.tearDown()
|
||||
|
@ -120,7 +120,8 @@ class MentatTests: XCTestCase {
|
|||
[: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 data = """
|
||||
|
@ -142,7 +143,8 @@ class MentatTests: XCTestCase {
|
|||
[: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)
|
||||
} catch {
|
||||
assertionFailure(error.localizedDescription)
|
||||
|
@ -317,7 +319,7 @@ class MentatTests: XCTestCase {
|
|||
guard let rows = relResult else {
|
||||
return assertionFailure("No results received")
|
||||
}
|
||||
|
||||
|
||||
var i = 0
|
||||
rows.forEach({ (row) in
|
||||
let (name, category) = expectedResults[i]
|
||||
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue