From f335253d4c35a92a443f7e05b33e8d90a1fe687e Mon Sep 17 00:00:00 2001 From: Thom Date: Mon, 25 Jun 2018 12:10:11 -0700 Subject: [PATCH] Fix known leaks and memory safety issues in both swift and android SDKs (#745) --- .gitignore | 2 +- ffi/src/lib.rs | 781 ++++++++++++------ ffi/src/utils.rs | 94 +++ scripts/ios_build.sh | 1 + sdks/android/Mentat/build.gradle | 2 +- .../mozilla/mentat/FFIIntegrationTest.java | 14 +- .../org/mozilla/mentat/AttributeList.java | 48 -- .../org/mozilla/mentat/ColResultIterator.java | 34 +- .../java/org/mozilla/mentat/CollResult.java | 12 +- .../org/mozilla/mentat/EntityBuilder.java | 159 ++-- .../java/org/mozilla/mentat/InProgress.java | 59 +- .../org/mozilla/mentat/InProgressBuilder.java | 158 ++-- .../mentat/InProgressTransactionResult.java | 44 +- .../src/main/java/org/mozilla/mentat/JNA.java | 233 +++--- .../main/java/org/mozilla/mentat/Mentat.java | 117 ++- .../main/java/org/mozilla/mentat/Query.java | 118 ++- .../java/org/mozilla/mentat/RelResult.java | 25 +- .../org/mozilla/mentat/RelResultIterator.java | 34 +- .../java/org/mozilla/mentat/RustError.java | 86 ++ .../java/org/mozilla/mentat/RustIterator.java | 70 ++ .../java/org/mozilla/mentat/RustObject.java | 87 +- .../java/org/mozilla/mentat/RustResult.java | 62 -- .../java/org/mozilla/mentat/TupleResult.java | 41 +- .../java/org/mozilla/mentat/TxChange.java | 23 +- .../java/org/mozilla/mentat/TxChangeList.java | 20 +- .../java/org/mozilla/mentat/TxReport.java | 25 +- .../java/org/mozilla/mentat/TypedValue.java | 41 +- .../Mentat/Mentat.xcodeproj/project.pbxproj | 16 +- .../swift/Mentat/Mentat/Core/TypedValue.swift | 8 +- .../Mentat/Extensions/Result+Unwrap.swift | 50 -- .../Mentat/Extensions/RustError+Unwrap.swift | 48 ++ .../Mentat/Extensions/String+Destroying.swift | 21 + .../Mentat/Extensions/UUID+Destroying.swift | 21 + sdks/swift/Mentat/Mentat/Mentat.swift | 41 +- sdks/swift/Mentat/Mentat/Query/Query.swift | 42 +- .../swift/Mentat/Mentat/Query/RelResult.swift | 2 +- .../Mentat/Mentat/Query/TupleResult.swift | 9 +- .../swift/Mentat/Mentat/Rust/RustObject.swift | 4 +- .../Mentat/Transact/EntityBuilder.swift | 77 +- .../Mentat/Mentat/Transact/InProgress.swift | 19 +- .../Mentat/Transact/InProgressBuilder.swift | 77 +- .../Mentat/Mentat/Transact/TxReport.swift | 4 +- sdks/swift/Mentat/Mentat/store.h | 207 ++--- 43 files changed, 1711 insertions(+), 1325 deletions(-) delete mode 100644 sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/AttributeList.java create mode 100644 sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/RustError.java create mode 100644 sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/RustIterator.java delete mode 100644 sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/RustResult.java delete mode 100644 sdks/swift/Mentat/Mentat/Extensions/Result+Unwrap.swift create mode 100644 sdks/swift/Mentat/Mentat/Extensions/RustError+Unwrap.swift create mode 100644 sdks/swift/Mentat/Mentat/Extensions/String+Destroying.swift create mode 100644 sdks/swift/Mentat/Mentat/Extensions/UUID+Destroying.swift diff --git a/.gitignore b/.gitignore index 541b49d1..9c07c6b5 100644 --- a/.gitignore +++ b/.gitignore @@ -81,7 +81,7 @@ build.xcarchive ## Obj-C/Swift specific *.hmap *.ipa - +/sdks/swift/Mentat/External-Dependencies # Android & IntelliJ **/*.iml diff --git a/ffi/src/lib.rs b/ffi/src/lib.rs index a665b25a..b08d7d38 100644 --- a/ffi/src/lib.rs +++ b/ffi/src/lib.rs @@ -62,10 +62,12 @@ //! wrapped inside a Rust closure and added to a [TxObserver](mentat::TxObserver) struct. This is then used to //! register the observer with the store. //! -//! [Result](std::result::Result) and [Option](std::option::Option) Rust types have `repr(C)` structs that mirror them. This is to provide a more -//! native access pattern to callers and to enable easier passing of optional types and error -//! propogation. These types have implemented [From](std::convert::From) such that conversion from the Rust type -//! to the C type is as painless as possible. +//! Functions that may fail take an out parameter of type `*mut ExternError`. In the event the +//! function fails, information about the error that occured will be stored inside it (and, +//! typically, a null pointer will be returned). Convenience functions for unpacking a +//! `Result` as a `*mut T` while writing any error to the `ExternError` are provided as +//! `translate_result`, `translate_opt_result` (for `Result>`) and `translate_void_result` +//! (for `Result<(), T>`). Callers are responsible for freeing the `message` field of `ExternError`. extern crate core; extern crate libc; @@ -80,6 +82,7 @@ use std::os::raw::{ c_char, c_int, c_longlong, + c_ulonglong, c_void, }; use std::slice; @@ -87,6 +90,7 @@ use std::sync::{ Arc, }; use std::vec; +use std::ffi::CString; pub use mentat::{ Binding, @@ -128,116 +132,76 @@ pub use utils::strings::{ string_to_c_char, }; +use utils::error::{ + ExternError, + translate_result, + translate_opt_result, + translate_void_result, +}; + pub use utils::log; // type aliases for iterator types. pub type BindingIterator = vec::IntoIter; pub type BindingListIterator = std::slice::Chunks<'static, mentat::Binding>; +/// Helper macro for asserting one or more pointers are not null at the same time. +#[macro_export] +macro_rules! assert_not_null { + ($($e:expr),+ $(,)*) => ($( + assert!(!$e.is_null(), concat!("Unexpected null pointer: ", stringify!($e))); + )+); +} + /// A C representation of the change provided by the transaction observers /// from a single transact. /// Holds a transaction identifier, the changes as a set of affected attributes /// and the length of the list of changes. -/// -/// #Safety -/// -/// Callers are responsible for managing the memory for the return value. -/// A destructor `destroy` is provided for releasing the memory for this -/// pointer type. #[repr(C)] #[derive(Debug, Clone)] pub struct TransactionChange { pub txid: Entid, - pub changes_len: usize, - pub changes: Box<[Entid]>, + pub changes: *const c_longlong, + pub changes_len: c_ulonglong, } - /// A C representation of the list of changes provided by the transaction observers. - /// Provides the list of changes as the length of the list. -/// -/// #Safety -/// -/// Callers are responsible for managing the memory for the return value. -/// A destructor `destroy` is provided for releasing the memory for this -/// pointer type. +/// A C representation of the list of changes provided by the transaction observers. +/// Provides the list of changes as the length of the list. #[repr(C)] #[derive(Debug)] pub struct TxChangeList { - pub reports: Box<[TransactionChange]>, - pub len: usize, -} - -/// A C representation Rust's [Option](std::option::Option). -/// A value of `Some` results in `value` containing a raw pointer as a `c_void`. -/// A value of `None` results in `value` containing a null pointer. -/// -/// #Safety -/// -/// Callers are responsible for managing the memory for the return value. -/// A destructor `destroy` is provided for releasing the memory for this -/// pointer type. -#[repr(C)] -#[derive(Debug)] -pub struct ExternOption { - pub value: *mut c_void, -} - -impl From> for ExternOption { - fn from(option: Option) -> Self { - ExternOption { - value: option.map_or(std::ptr::null_mut(), |v| Box::into_raw(Box::new(v)) as *mut _ as *mut c_void) - } - } -} - -/// A C representation Rust's [Result](std::result::Result). -/// A value of `Ok` results in `ok` containing a raw pointer as a `c_void` -/// and `err` containing a null pointer. -/// A value of `Err` results in `value` containing a null pointer and `err` containing an error message. -/// -/// #Safety -/// -/// Callers are responsible for managing the memory for the return value. -/// A destructor `destroy` is provided for releasing the memory for this -/// pointer type. -#[repr(C)] -#[derive(Debug)] -pub struct ExternResult { - pub ok: *const c_void, - pub err: *const c_char, -} - -impl From> for ExternResult where E: Display { - fn from(result: Result) -> Self { - match result { - Ok(value) => { - ExternResult { - err: std::ptr::null(), - ok: Box::into_raw(Box::new(value)) as *const _ as *const c_void, - } - }, - Err(e) => { - ExternResult { - err: string_to_c_char(e.to_string()), - ok: std::ptr::null(), - } - } - } - } + pub reports: *const TransactionChange, + pub len: c_ulonglong, } #[repr(C)] #[derive(Debug)] pub struct InProgressTransactResult<'a, 'c> { pub in_progress: *mut InProgress<'a, 'c>, - pub result: *mut ExternResult, + pub tx_report: *mut TxReport, + // TODO: This is a different usage pattern than most uses of ExternError. Is this bad? + pub err: ExternError, +} + +impl<'a, 'c> InProgressTransactResult<'a, 'c> { + // This takes a tuple so that we can pass the result of `transact()` into it directly. + unsafe fn from_transact(tuple: (InProgress<'a, 'c>, Result)) -> Self { + let (in_progress, tx_result) = tuple; + let mut err = ExternError::default(); + let tx_report = translate_result(tx_result, (&mut err) as *mut ExternError); + InProgressTransactResult { + in_progress: Box::into_raw(Box::new(in_progress)), + tx_report, + err + } + } } /// 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 +/// therefore the caller is responsible for calling `store_destroy` to release the memory /// used by the [Store](mentat::Store) in order to avoid a memory leak. -// TODO: Start returning `ExternResult`s rather than crashing on error. +// TODO: Take an `ExternError` parameter, rather than crashing on error. /// /// # Safety /// @@ -246,6 +210,7 @@ pub struct InProgressTransactResult<'a, 'c> { /// pointer type. #[no_mangle] pub extern "C" fn store_open(uri: *const c_char) -> *mut Store { + assert_not_null!(uri); let uri = c_char_to_string(uri); let store = Store::open(&uri).expect("expected a store"); Box::into_raw(Box::new(store)) @@ -273,27 +238,26 @@ pub extern "C" fn store_open_encrypted(uri: *const c_char, key: *const c_char) - /// performed together. This is more efficient than performing /// a large set of individual commits. /// -/// Returns a [Result](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 +/// A destructor `in_progress_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 { +pub unsafe extern "C" fn store_begin_transaction<'a, 'c>(store: *mut Store, error: *mut ExternError) -> *mut InProgress<'a, 'c> { + assert_not_null!(store); let store = &mut *store; - Box::into_raw(Box::new(store.begin_transaction().into())) + translate_result(store.begin_transaction(), error) } /// Perform a single transact operation using the current in progress /// transaction. Takes edn as a string to transact. /// -/// Returns a [Result](mentat::TxReport) as an [ExternResult](ExternResult). +/// Returns a [Result](mentat::TxReport) /// /// # Safety /// @@ -303,34 +267,33 @@ pub unsafe extern "C" fn store_begin_transaction(store: *mut Store) -> *mut Exte /// /// 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 { +pub unsafe extern "C" fn in_progress_transact<'m>(in_progress: *mut InProgress<'m, 'm>, transaction: *const c_char, error: *mut ExternError) -> *mut TxReport { + assert_not_null!(in_progress); let in_progress = &mut *in_progress; let transaction = c_char_to_string(transaction); - Box::into_raw(Box::new(in_progress.transact(transaction).into())) + translate_result(in_progress.transact(transaction), error) } /// Commit all the transacts that have been performed using this /// in progress transaction. /// -/// Returns a [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 { +pub unsafe extern "C" fn in_progress_commit<'m>(in_progress: *mut InProgress<'m, 'm>, error: *mut ExternError) { + assert_not_null!(in_progress); let in_progress = Box::from_raw(in_progress); - Box::into_raw(Box::new(in_progress.commit().into())) + translate_void_result(in_progress.commit(), error); } /// Rolls back all the transacts that have been performed using this /// in progress transaction. /// -/// Returns a [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 { +pub unsafe extern "C" fn in_progress_rollback<'m>(in_progress: *mut InProgress<'m, 'm>, error: *mut ExternError) { + assert_not_null!(in_progress); let in_progress = Box::from_raw(in_progress); - Box::into_raw(Box::new(in_progress.rollback().into())) + translate_void_result(in_progress.rollback(), error); } /// Creates a builder using the in progress transaction to allow for programmatic @@ -343,6 +306,7 @@ pub unsafe extern "C" fn in_progress_rollback<'m>(in_progress: *mut InProgress<' /// pointer type. #[no_mangle] pub unsafe extern "C" fn in_progress_builder<'m>(in_progress: *mut InProgress<'m, 'm>) -> *mut InProgressBuilder { + assert_not_null!(in_progress); let in_progress = Box::from_raw(in_progress); Box::into_raw(Box::new(in_progress.builder())) } @@ -357,6 +321,7 @@ pub unsafe extern "C" fn in_progress_builder<'m>(in_progress: *mut InProgress<'m /// 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 { + assert_not_null!(in_progress); 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))) @@ -372,6 +337,7 @@ pub unsafe extern "C" fn in_progress_entity_builder_from_temp_id<'m>(in_progress /// 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 { + assert_not_null!(in_progress); let in_progress = Box::from_raw(in_progress); Box::into_raw(Box::new(in_progress.builder().describe(&KnownEntid(entid)))) } @@ -385,12 +351,13 @@ pub unsafe extern "C" fn in_progress_entity_builder_from_entid<'m>(in_progress: /// 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 { +pub unsafe extern "C" fn store_in_progress_builder<'a, 'c>(store: *mut Store, error: *mut ExternError) -> *mut InProgressBuilder<'a, 'c> { + assert_not_null!(store); let store = &mut *store; let result = store.begin_transaction().and_then(|in_progress| { Ok(in_progress.builder()) }); - Box::into_raw(Box::new(result.into())) + translate_result(result, error) } /// Starts a new transaction and creates a builder for an entity with `tempid` @@ -402,13 +369,14 @@ pub unsafe extern "C" fn store_in_progress_builder(store: *mut Store) -> *mut Ex /// 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 { +pub unsafe extern "C" fn store_entity_builder_from_temp_id<'a, 'c>(store: *mut Store, temp_id: *const c_char, error: *mut ExternError) -> *mut EntityBuilder> { + assert_not_null!(store); 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())) + translate_result(result, error) } /// Starts a new transaction and creates a builder for an entity with `entid` @@ -420,12 +388,13 @@ pub unsafe extern "C" fn store_entity_builder_from_temp_id(store: *mut Store, te /// 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 { +pub unsafe extern "C" fn store_entity_builder_from_entid<'a, 'c>(store: *mut Store, entid: c_longlong, error: *mut ExternError) -> *mut EntityBuilder> { + assert_not_null!(store); 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())) + translate_result(result, error) } /// Uses `builder` to assert `value` for `kw` on entity `entid`. @@ -438,11 +407,18 @@ pub unsafe extern "C" fn store_entity_builder_from_entid(store: *mut Store, enti /// // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[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 { +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, + error: *mut ExternError +) { + assert_not_null!(builder); 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())) + translate_void_result(builder.add_kw(KnownEntid(entid), &kw, value), error); } /// Uses `builder` to assert `value` for `kw` on entity `entid`. @@ -454,11 +430,18 @@ pub unsafe extern "C" fn in_progress_builder_add_string<'a, 'c>(builder: *mut In /// If the `:db/type` of the attribute described by `kw` is not `:db.type/long`. // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[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 { +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, + error: *mut ExternError +) { + assert_not_null!(builder); 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())) + translate_void_result(builder.add_kw(KnownEntid(entid), &kw, value), error); } /// Uses `builder` to assert `value` for `kw` on entity `entid`. @@ -471,11 +454,18 @@ pub unsafe extern "C" fn in_progress_builder_add_long<'a, 'c>(builder: *mut InPr /// If the `:db/type` of the attribute described by `kw` is not `:db.type/ref`. // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[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 { +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, + error: *mut ExternError +) { + assert_not_null!(builder); 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())) + translate_void_result(builder.add_kw(KnownEntid(entid), &kw, value), error); } /// Uses `builder` to assert `value` for `kw` on entity `entid`. @@ -489,11 +479,18 @@ pub unsafe extern "C" fn in_progress_builder_add_ref<'a, 'c>(builder: *mut InPro /// // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[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 { +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, + error: *mut ExternError +) { + assert_not_null!(builder); 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())) + translate_void_result(builder.add_kw(KnownEntid(entid), &kw, value), error); } /// Uses `builder` to assert `value` for `kw` on entity `entid`. @@ -506,11 +503,18 @@ pub unsafe extern "C" fn in_progress_builder_add_keyword<'a, 'c>(builder: *mut I /// // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[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 { +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, + error: *mut ExternError +) { + assert_not_null!(builder); 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())) + translate_void_result(builder.add_kw(KnownEntid(entid), &kw, value), error); } /// Uses `builder` to assert `value` for `kw` on entity `entid`. @@ -523,11 +527,18 @@ pub unsafe extern "C" fn in_progress_builder_add_boolean<'a, 'c>(builder: *mut I /// // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[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 { +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, + error: *mut ExternError +) { + assert_not_null!(builder); 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())) + translate_void_result(builder.add_kw(KnownEntid(entid), &kw, value), error); } /// Uses `builder` to assert `value` for `kw` on entity `entid`. @@ -540,11 +551,18 @@ pub unsafe extern "C" fn in_progress_builder_add_double<'a, 'c>(builder: *mut In /// // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[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 { +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, + error: *mut ExternError +) { + assert_not_null!(builder); 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())) + translate_void_result(builder.add_kw(KnownEntid(entid), &kw, value), error); } /// Uses `builder` to assert `value` for `kw` on entity `entid`. @@ -557,13 +575,20 @@ pub unsafe extern "C" fn in_progress_builder_add_timestamp<'a, 'c>(builder: *mut /// // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[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 { +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: *const [u8; 16], + error: *mut ExternError +) { + assert_not_null!(builder, value); 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())) + translate_void_result(builder.add_kw(KnownEntid(entid), &kw, value), error); } /// Uses `builder` to retract `value` for `kw` on entity `entid`. @@ -576,11 +601,18 @@ pub unsafe extern "C" fn in_progress_builder_add_uuid<'a, 'c>(builder: *mut InPr /// // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[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 { +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, + error: *mut ExternError +) { + assert_not_null!(builder); 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())) + translate_void_result(builder.retract_kw(KnownEntid(entid), &kw, value), error); } /// Uses `builder` to retract `value` for `kw` on entity `entid`. @@ -593,11 +625,18 @@ pub unsafe extern "C" fn in_progress_builder_retract_string<'a, 'c>(builder: *mu /// // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[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 { +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, + error: *mut ExternError +) { + assert_not_null!(builder); 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())) + translate_void_result(builder.retract_kw(KnownEntid(entid), &kw, value), error); } /// Uses `builder` to retract `value` for `kw` on entity `entid`. @@ -610,11 +649,18 @@ pub unsafe extern "C" fn in_progress_builder_retract_long<'a, 'c>(builder: *mut /// // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[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 { +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, + error: *mut ExternError +) { + assert_not_null!(builder); 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())) + translate_void_result(builder.retract_kw(KnownEntid(entid), &kw, value), error); } @@ -628,11 +674,18 @@ pub unsafe extern "C" fn in_progress_builder_retract_ref<'a, 'c>(builder: *mut I /// // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[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 { +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, + error: *mut ExternError +) { + assert_not_null!(builder); 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())) + translate_void_result(builder.retract_kw(KnownEntid(entid), &kw, value), error); } /// Uses `builder` to retract `value` for `kw` on entity `entid`. @@ -645,11 +698,18 @@ pub unsafe extern "C" fn in_progress_builder_retract_keyword<'a, 'c>(builder: *m /// // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[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 { +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, + error: *mut ExternError +) { + assert_not_null!(builder); 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())) + translate_void_result(builder.retract_kw(KnownEntid(entid), &kw, value), error); } /// Uses `builder` to retract `value` for `kw` on entity `entid`. @@ -662,11 +722,18 @@ pub unsafe extern "C" fn in_progress_builder_retract_boolean<'a, 'c>(builder: *m /// // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[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 { +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, + error: *mut ExternError +) { + assert_not_null!(builder); 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())) + translate_void_result(builder.retract_kw(KnownEntid(entid), &kw, value), error); } /// Uses `builder` to retract `value` for `kw` on entity `entid`. @@ -679,11 +746,18 @@ pub unsafe extern "C" fn in_progress_builder_retract_double<'a, 'c>(builder: *mu /// // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[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 { +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, + error: *mut ExternError +) { + assert_not_null!(builder); 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())) + translate_void_result(builder.retract_kw(KnownEntid(entid), &kw, value), error); } /// Uses `builder` to retract `value` for `kw` on entity `entid`. @@ -698,13 +772,20 @@ pub unsafe extern "C" fn in_progress_builder_retract_timestamp<'a, 'c>(builder: // // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[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 { +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: *const [u8; 16], + error: *mut ExternError +) { + assert_not_null!(builder, value); 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())) + translate_void_result(builder.retract_kw(KnownEntid(entid), &kw, value), error); } /// Transacts and commits all the assertions and retractions that have been performed @@ -712,13 +793,12 @@ pub unsafe extern "C" fn in_progress_builder_retract_uuid<'a, 'c>(builder: *mut /// /// This consumes the builder and the enclosed [InProgress](mentat::InProgress) transaction. /// -/// Returns a [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 { +pub unsafe extern "C" fn in_progress_builder_commit<'a, 'c>(builder: *mut InProgressBuilder<'a, 'c>, error: *mut ExternError) -> *mut TxReport { + assert_not_null!(builder); let builder = Box::from_raw(builder); - Box::into_raw(Box::new(builder.commit().into())) + translate_result(builder.commit(), error) } /// Transacts all the assertions and retractions that have been performed @@ -736,11 +816,10 @@ pub unsafe extern "C" fn in_progress_builder_commit<'a, 'c>(builder: *mut InProg /// // 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> { +pub unsafe extern "C" fn in_progress_builder_transact<'a, 'c>(builder: *mut InProgressBuilder<'a, 'c>) -> InProgressTransactResult<'a, 'c> { + assert_not_null!(builder); 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)) + InProgressTransactResult::from_transact(builder.transact()) } /// Uses `builder` to assert `value` for `kw` on entity `entid`. @@ -753,11 +832,17 @@ pub unsafe extern "C" fn in_progress_builder_transact<'a, 'c>(builder: *mut InPr /// // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] -pub unsafe extern "C" fn entity_builder_add_string<'a, 'c>(builder: *mut EntityBuilder>, kw: *const c_char, value: *const c_char) -> *mut ExternResult { +pub unsafe extern "C" fn entity_builder_add_string<'a, 'c>( + builder: *mut EntityBuilder>, + kw: *const c_char, + value: *const c_char, + error: *mut ExternError +) { + assert_not_null!(builder); 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())) + translate_void_result(builder.add_kw(&kw, value), error); } /// Uses `builder` to assert `value` for `kw` on entity `entid`. @@ -770,11 +855,17 @@ pub unsafe extern "C" fn entity_builder_add_string<'a, 'c>(builder: *mut EntityB /// // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] -pub unsafe extern "C" fn entity_builder_add_long<'a, 'c>(builder: *mut EntityBuilder>, kw: *const c_char, value: c_longlong) -> *mut ExternResult { +pub unsafe extern "C" fn entity_builder_add_long<'a, 'c>( + builder: *mut EntityBuilder>, + kw: *const c_char, + value: c_longlong, + error: *mut ExternError +) { + assert_not_null!(builder); 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())) + translate_void_result(builder.add_kw(&kw, value), error); } /// Uses `builder` to assert `value` for `kw` on entity `entid`. @@ -787,11 +878,17 @@ pub unsafe extern "C" fn entity_builder_add_long<'a, 'c>(builder: *mut EntityBui /// // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] -pub unsafe extern "C" fn entity_builder_add_ref<'a, 'c>(builder: *mut EntityBuilder>, kw: *const c_char, value: c_longlong) -> *mut ExternResult { +pub unsafe extern "C" fn entity_builder_add_ref<'a, 'c>( + builder: *mut EntityBuilder>, + kw: *const c_char, + value: c_longlong, + error: *mut ExternError +) { + assert_not_null!(builder); 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())) + translate_void_result(builder.add_kw(&kw, value), error); } /// Uses `builder` to assert `value` for `kw` on entity `entid`. @@ -804,11 +901,17 @@ pub unsafe extern "C" fn entity_builder_add_ref<'a, 'c>(builder: *mut EntityBuil /// // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] -pub unsafe extern "C" fn entity_builder_add_keyword<'a, 'c>(builder: *mut EntityBuilder>, kw: *const c_char, value: *const c_char) -> *mut ExternResult { +pub unsafe extern "C" fn entity_builder_add_keyword<'a, 'c>( + builder: *mut EntityBuilder>, + kw: *const c_char, + value: *const c_char, + error: *mut ExternError +) { + assert_not_null!(builder); 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())) + translate_void_result(builder.add_kw(&kw, value), error); } /// Uses `builder` to assert `value` for `kw` on entity `entid`. @@ -821,11 +924,17 @@ pub unsafe extern "C" fn entity_builder_add_keyword<'a, 'c>(builder: *mut Entity /// // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] -pub unsafe extern "C" fn entity_builder_add_boolean<'a, 'c>(builder: *mut EntityBuilder>, kw: *const c_char, value: bool) -> *mut ExternResult { +pub unsafe extern "C" fn entity_builder_add_boolean<'a, 'c>( + builder: *mut EntityBuilder>, + kw: *const c_char, + value: bool, + error: *mut ExternError +) { + assert_not_null!(builder); 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())) + translate_void_result(builder.add_kw(&kw, value), error); } /// Uses `builder` to assert `value` for `kw` on entity `entid`. @@ -838,11 +947,17 @@ pub unsafe extern "C" fn entity_builder_add_boolean<'a, 'c>(builder: *mut Entity /// // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] -pub unsafe extern "C" fn entity_builder_add_double<'a, 'c>(builder: *mut EntityBuilder>, kw: *const c_char, value: f64) -> *mut ExternResult { +pub unsafe extern "C" fn entity_builder_add_double<'a, 'c>( + builder: *mut EntityBuilder>, + kw: *const c_char, + value: f64, + error: *mut ExternError +) { + assert_not_null!(builder); 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())) + translate_void_result(builder.add_kw(&kw, value), error); } /// Uses `builder` to assert `value` for `kw` on entity `entid`. @@ -855,11 +970,17 @@ pub unsafe extern "C" fn entity_builder_add_double<'a, 'c>(builder: *mut EntityB /// // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] -pub unsafe extern "C" fn entity_builder_add_timestamp<'a, 'c>(builder: *mut EntityBuilder>, kw: *const c_char, value: c_longlong) -> *mut ExternResult { +pub unsafe extern "C" fn entity_builder_add_timestamp<'a, 'c>( + builder: *mut EntityBuilder>, + kw: *const c_char, + value: c_longlong, + error: *mut ExternError +) { + assert_not_null!(builder); 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())) + translate_void_result(builder.add_kw(&kw, value), error); } /// Uses `builder` to assert `value` for `kw` on entity `entid`. @@ -872,13 +993,19 @@ pub unsafe extern "C" fn entity_builder_add_timestamp<'a, 'c>(builder: *mut Enti /// // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] -pub unsafe extern "C" fn entity_builder_add_uuid<'a, 'c>(builder: *mut EntityBuilder>, kw: *const c_char, value: *mut [u8; 16]) -> *mut ExternResult { +pub unsafe extern "C" fn entity_builder_add_uuid<'a, 'c>( + builder: *mut EntityBuilder>, + kw: *const c_char, + value: *const [u8; 16], + error: *mut ExternError +) { + assert_not_null!(builder); 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())) + translate_void_result(builder.add_kw(&kw, value), error); } /// Uses `builder` to retract `value` for `kw` on entity `entid`. @@ -891,11 +1018,17 @@ pub unsafe extern "C" fn entity_builder_add_uuid<'a, 'c>(builder: *mut EntityBui /// // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] -pub unsafe extern "C" fn entity_builder_retract_string<'a, 'c>(builder: *mut EntityBuilder>, kw: *const c_char, value: *const c_char) -> *mut ExternResult { +pub unsafe extern "C" fn entity_builder_retract_string<'a, 'c>( + builder: *mut EntityBuilder>, + kw: *const c_char, + value: *const c_char, + error: *mut ExternError +) { + assert_not_null!(builder); 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())) + translate_void_result(builder.retract_kw(&kw, value), error); } /// Uses `builder` to retract `value` for `kw` on entity `entid`. @@ -908,11 +1041,17 @@ pub unsafe extern "C" fn entity_builder_retract_string<'a, 'c>(builder: *mut Ent /// // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] -pub unsafe extern "C" fn entity_builder_retract_long<'a, 'c>(builder: *mut EntityBuilder>, kw: *const c_char, value: c_longlong) -> *mut ExternResult { +pub unsafe extern "C" fn entity_builder_retract_long<'a, 'c>( + builder: *mut EntityBuilder>, + kw: *const c_char, + value: c_longlong, + error: *mut ExternError +) { + assert_not_null!(builder); 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())) + translate_void_result(builder.retract_kw(&kw, value), error); } /// Uses `builder` to retract `value` for `kw` on entity `entid`. @@ -925,11 +1064,17 @@ pub unsafe extern "C" fn entity_builder_retract_long<'a, 'c>(builder: *mut Entit /// // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] -pub unsafe extern "C" fn entity_builder_retract_ref<'a, 'c>(builder: *mut EntityBuilder>, kw: *const c_char, value: c_longlong) -> *mut ExternResult { +pub unsafe extern "C" fn entity_builder_retract_ref<'a, 'c>( + builder: *mut EntityBuilder>, + kw: *const c_char, + value: c_longlong, + error: *mut ExternError +) { + assert_not_null!(builder); 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())) + translate_void_result(builder.retract_kw(&kw, value), error); } /// Uses `builder` to retract `value` for `kw` on entity `entid`. @@ -942,11 +1087,17 @@ pub unsafe extern "C" fn entity_builder_retract_ref<'a, 'c>(builder: *mut Entity /// // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] -pub unsafe extern "C" fn entity_builder_retract_keyword<'a, 'c>(builder: *mut EntityBuilder>, kw: *const c_char, value: *const c_char) -> *mut ExternResult { +pub unsafe extern "C" fn entity_builder_retract_keyword<'a, 'c>( + builder: *mut EntityBuilder>, + kw: *const c_char, + value: *const c_char, + error: *mut ExternError +) { + assert_not_null!(builder); 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())) + translate_void_result(builder.retract_kw(&kw, value), error); } /// Uses `builder` to retract `value` for `kw` on entity `entid`. @@ -959,11 +1110,17 @@ pub unsafe extern "C" fn entity_builder_retract_keyword<'a, 'c>(builder: *mut En /// // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] -pub unsafe extern "C" fn entity_builder_retract_boolean<'a, 'c>(builder: *mut EntityBuilder>, kw: *const c_char, value: bool) -> *mut ExternResult { +pub unsafe extern "C" fn entity_builder_retract_boolean<'a, 'c>( + builder: *mut EntityBuilder>, + kw: *const c_char, + value: bool, + error: *mut ExternError +) { + assert_not_null!(builder); 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())) + translate_void_result(builder.retract_kw(&kw, value), error); } /// Uses `builder` to retract `value` for `kw` on entity `entid`. @@ -976,11 +1133,17 @@ pub unsafe extern "C" fn entity_builder_retract_boolean<'a, 'c>(builder: *mut En /// // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] -pub unsafe extern "C" fn entity_builder_retract_double<'a, 'c>(builder: *mut EntityBuilder>, kw: *const c_char, value: f64) -> *mut ExternResult { +pub unsafe extern "C" fn entity_builder_retract_double<'a, 'c>( + builder: *mut EntityBuilder>, + kw: *const c_char, + value: f64, + error: *mut ExternError +) { + assert_not_null!(builder); 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())) + translate_void_result(builder.retract_kw(&kw, value), error); } /// Uses `builder` to retract `value` for `kw` on entity `entid`. @@ -993,11 +1156,17 @@ pub unsafe extern "C" fn entity_builder_retract_double<'a, 'c>(builder: *mut Ent /// // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] -pub unsafe extern "C" fn entity_builder_retract_timestamp<'a, 'c>(builder: *mut EntityBuilder>, kw: *const c_char, value: c_longlong) -> *mut ExternResult { +pub unsafe extern "C" fn entity_builder_retract_timestamp<'a, 'c>( + builder: *mut EntityBuilder>, + kw: *const c_char, + value: c_longlong, + error: *mut ExternError +) { + assert_not_null!(builder); 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())) + translate_void_result(builder.retract_kw(&kw, value), error); } /// Uses `builder` to retract `value` for `kw` on entity `entid`. @@ -1011,13 +1180,19 @@ pub unsafe extern "C" fn entity_builder_retract_timestamp<'a, 'c>(builder: *mut // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 // 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>, kw: *const c_char, value: *mut [u8; 16]) -> *mut ExternResult { +pub unsafe extern "C" fn entity_builder_retract_uuid<'a, 'c>( + builder: *mut EntityBuilder>, + kw: *const c_char, + value: *const [u8; 16], + error: *mut ExternError +) { + assert_not_null!(builder, value); 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())) + translate_void_result(builder.retract_kw(&kw, value), error); } /// Transacts all the assertions and retractions that have been performed @@ -1035,11 +1210,10 @@ pub unsafe extern "C" fn entity_builder_retract_uuid<'a, 'c>(builder: *mut Entit /// /// TODO: Document the errors that can result from transact #[no_mangle] -pub unsafe extern "C" fn entity_builder_transact<'a, 'c>(builder: *mut EntityBuilder>) -> *mut InProgressTransactResult<'a, 'c> { +pub unsafe extern "C" fn entity_builder_transact<'a, 'c>(builder: *mut EntityBuilder>) -> InProgressTransactResult<'a, 'c> { + assert_not_null!(builder); 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)) + InProgressTransactResult::from_transact(builder.transact()) } /// Transacts and commits all the assertions and retractions that have been performed @@ -1047,21 +1221,20 @@ pub unsafe extern "C" fn entity_builder_transact<'a, 'c>(builder: *mut EntityBui /// /// This consumes the builder and the enclosed [InProgress](mentat::InProgress) transaction. /// -/// Returns a [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>) -> *mut ExternResult { +pub unsafe extern "C" fn entity_builder_commit<'a, 'c>(builder: *mut EntityBuilder>, error: *mut ExternError) -> *mut TxReport { + assert_not_null!(builder); let builder = Box::from_raw(builder); - Box::into_raw(Box::new(builder.commit().into())) + translate_result(builder.commit(), error) } /// Performs a single transaction against the store. /// -/// Returns a [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 { +pub unsafe extern "C" fn store_transact(store: *mut Store, transaction: *const c_char, error: *mut ExternError) -> *mut TxReport { + assert_not_null!(store); let store = &mut *store; let transaction = c_char_to_string(transaction); let result = store.begin_transaction().and_then(|mut in_progress| { @@ -1070,12 +1243,13 @@ pub unsafe extern "C" fn store_transact(store: *mut Store, transaction: *const c .map(|_| tx_report) }) }); - Box::into_raw(Box::new(result.into())) + translate_result(result, error) } /// 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 { + assert_not_null!(tx_report); let tx_report = &*tx_report; tx_report.tx_id as c_longlong } @@ -1083,14 +1257,20 @@ pub unsafe extern "C" fn tx_report_get_entid(tx_report: *mut TxReport) -> c_long /// Fetches the `tx_instant` for the given [TxReport](mentat::TxReport). #[no_mangle] pub unsafe extern "C" fn tx_report_get_tx_instant(tx_report: *mut TxReport) -> c_longlong { + assert_not_null!(tx_report); let tx_report = &*tx_report; tx_report.tx_instant.timestamp() as c_longlong } /// Fetches the [Entid](mentat::Entid) assigned to the `tempid` during the transaction represented /// by the given [TxReport](mentat::TxReport). +/// +/// Note that this reutrns the value as a heap allocated pointer that the caller is responsible for +/// freeing with `destroy()`. +// TODO: This is gross and unnecessary #[no_mangle] pub unsafe extern "C" fn tx_report_entity_for_temp_id(tx_report: *mut TxReport, tempid: *const c_char) -> *mut c_longlong { + assert_not_null!(tx_report); let tx_report = &*tx_report; let key = c_char_to_string(tempid); if let Some(entid) = tx_report.tempids.get(key) { @@ -1104,20 +1284,22 @@ pub unsafe extern "C" fn tx_report_entity_for_temp_id(tx_report: *mut TxReport, /// `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 }; +pub unsafe extern "C" fn store_cache_attribute_forward(store: *mut Store, attribute: *const c_char, error: *mut ExternError) { + assert_not_null!(store); + let store = &mut *store; let kw = kw_from_string(c_char_to_string(attribute)); - Box::into_raw(Box::new(store.cache(&kw, CacheDirection::Forward).into())) + translate_void_result(store.cache(&kw, CacheDirection::Forward), error); } /// 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 }; +pub unsafe extern "C" fn store_cache_attribute_reverse(store: *mut Store, attribute: *const c_char, error: *mut ExternError) { + assert_not_null!(store); + let store = &mut *store; let kw = kw_from_string(c_char_to_string(attribute)); - Box::into_raw(Box::new(store.cache(&kw, CacheDirection::Reverse).into())) + translate_void_result(store.cache(&kw, CacheDirection::Reverse), error); } /// Adds an attribute to the cache. @@ -1129,10 +1311,11 @@ pub extern "C" fn store_cache_attribute_reverse(store: *mut Store, attribute: *c /// `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 }; +pub unsafe extern "C" fn store_cache_attribute_bi_directional(store: *mut Store, attribute: *const c_char, error: *mut ExternError) { + assert_not_null!(store); + let store = &mut *store; let kw = kw_from_string(c_char_to_string(attribute)); - Box::into_raw(Box::new(store.cache(&kw, CacheDirection::Both).into())) + translate_void_result(store.cache(&kw, CacheDirection::Both), error); } /// Creates a [QueryBuilder](mentat::QueryBuilder) from the given store to execute the provided query. @@ -1146,10 +1329,10 @@ pub extern "C" fn store_cache_attribute_bi_directional(store: *mut Store, attrib /// TODO: Update QueryBuilder so it only takes a [Store](mentat::Store) pointer on execution #[no_mangle] pub unsafe extern "C" fn store_query<'a>(store: *mut Store, query: *const c_char) -> *mut QueryBuilder<'a> { + assert_not_null!(store); let query = c_char_to_string(query); let store = &mut *store; - let query_builder = QueryBuilder::new(store, query); - Box::into_raw(Box::new(query_builder)) + Box::into_raw(Box::new(QueryBuilder::new(store, query))) } /// Binds a [TypedValue::Long](mentat::TypedValue::Long) to a [Variable](mentat::Variable) with the given name. @@ -1157,9 +1340,10 @@ pub unsafe extern "C" fn store_query<'a>(store: *mut Store, query: *const c_char // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] pub unsafe extern "C" fn query_builder_bind_long(query_builder: *mut QueryBuilder, var: *const c_char, value: c_longlong) { + assert_not_null!(query_builder); let var = c_char_to_string(var); let query_builder = &mut *query_builder; - query_builder.bind_long(&var, value); + query_builder.bind_long(&var, value); } /// Binds a [TypedValue::Ref](mentat::TypedValue::Ref) to a [Variable](mentat::Variable) with the given name. @@ -1167,6 +1351,7 @@ pub unsafe extern "C" fn query_builder_bind_long(query_builder: *mut QueryBuilde // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] pub unsafe extern "C" fn query_builder_bind_ref(query_builder: *mut QueryBuilder, var: *const c_char, value: c_longlong) { + assert_not_null!(query_builder); let var = c_char_to_string(var); let query_builder = &mut *query_builder; query_builder.bind_ref(&var, value); @@ -1182,6 +1367,7 @@ pub unsafe extern "C" fn query_builder_bind_ref(query_builder: *mut QueryBuilder // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] pub unsafe extern "C" fn query_builder_bind_ref_kw(query_builder: *mut QueryBuilder, var: *const c_char, value: *const c_char) { + assert_not_null!(query_builder); let var = c_char_to_string(var); let kw = kw_from_string(c_char_to_string(value)); let query_builder = &mut *query_builder; @@ -1196,6 +1382,7 @@ pub unsafe extern "C" fn query_builder_bind_ref_kw(query_builder: *mut QueryBuil // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] pub unsafe extern "C" fn query_builder_bind_kw(query_builder: *mut QueryBuilder, var: *const c_char, value: *const c_char) { + assert_not_null!(query_builder); let var = c_char_to_string(var); let query_builder = &mut *query_builder; let kw = kw_from_string(c_char_to_string(value)); @@ -1207,6 +1394,7 @@ pub unsafe extern "C" fn query_builder_bind_kw(query_builder: *mut QueryBuilder, // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] pub unsafe extern "C" fn query_builder_bind_boolean(query_builder: *mut QueryBuilder, var: *const c_char, value: bool) { + assert_not_null!(query_builder); let var = c_char_to_string(var); let query_builder = &mut *query_builder; query_builder.bind_value(&var, value); @@ -1217,6 +1405,7 @@ pub unsafe extern "C" fn query_builder_bind_boolean(query_builder: *mut QueryBui // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] pub unsafe extern "C" fn query_builder_bind_double(query_builder: *mut QueryBuilder, var: *const c_char, value: f64) { + assert_not_null!(query_builder); let var = c_char_to_string(var); let query_builder = &mut *query_builder; query_builder.bind_value(&var, value); @@ -1228,6 +1417,7 @@ pub unsafe extern "C" fn query_builder_bind_double(query_builder: *mut QueryBuil // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] pub unsafe extern "C" fn query_builder_bind_timestamp(query_builder: *mut QueryBuilder, var: *const c_char, value: c_longlong) { + assert_not_null!(query_builder); let var = c_char_to_string(var); let query_builder = &mut *query_builder; query_builder.bind_instant(&var, value); @@ -1238,6 +1428,7 @@ pub unsafe extern "C" fn query_builder_bind_timestamp(query_builder: *mut QueryB // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] pub unsafe extern "C" fn query_builder_bind_string(query_builder: *mut QueryBuilder, var: *const c_char, value: *const c_char) { + assert_not_null!(query_builder); let var = c_char_to_string(var); let value = c_char_to_string(value); let query_builder = &mut *query_builder; @@ -1249,7 +1440,8 @@ pub unsafe extern "C" fn query_builder_bind_string(query_builder: *mut QueryBuil /// // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] -pub unsafe extern "C" fn query_builder_bind_uuid(query_builder: *mut QueryBuilder, var: *const c_char, value: *mut [u8; 16]) { +pub unsafe extern "C" fn query_builder_bind_uuid(query_builder: *mut QueryBuilder, var: *const c_char, value: *const [u8; 16]) { + assert_not_null!(query_builder, value); let var = c_char_to_string(var); let value = &*value; let value = Uuid::from_bytes(value).expect("valid uuid"); @@ -1266,18 +1458,14 @@ pub unsafe extern "C" fn query_builder_bind_uuid(query_builder: *mut QueryBuilde /// # 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 `typed_value_destroy` is provided for releasing the memory for this /// pointer type. #[no_mangle] -pub unsafe extern "C" fn query_builder_execute_scalar(query_builder: *mut QueryBuilder) -> *mut ExternResult { +pub unsafe extern "C" fn query_builder_execute_scalar(query_builder: *mut QueryBuilder, error: *mut ExternError) -> *mut Binding { + assert_not_null!(query_builder); let query_builder = &mut *query_builder; let results = query_builder.execute_scalar(); - let extern_result = match results { - Ok(Some(v)) => ExternResult { err: std::ptr::null(), ok: Box::into_raw(Box::new(v)) as *const _ as *const c_void, }, - Ok(None) => ExternResult { err: std::ptr::null(), ok: std::ptr::null(), }, - Err(e) => ExternResult { err: string_to_c_char(e.to_string()), ok: std::ptr::null(), } - }; - Box::into_raw(Box::new(extern_result)) + translate_opt_result(results, error) } /// Executes a query and returns the results as a [Coll](mentat::QueryResults::Coll). @@ -1289,13 +1477,14 @@ pub unsafe extern "C" fn query_builder_execute_scalar(query_builder: *mut QueryB /// # 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 `typed_value_list_destroy` is provided for releasing the memory for this /// pointer type. #[no_mangle] -pub unsafe extern "C" fn query_builder_execute_coll(query_builder: *mut QueryBuilder) -> *mut ExternResult { +pub unsafe extern "C" fn query_builder_execute_coll(query_builder: *mut QueryBuilder, error: *mut ExternError) -> *mut Vec { + assert_not_null!(query_builder); let query_builder = &mut *query_builder; let results = query_builder.execute_coll(); - Box::into_raw(Box::new(results.into())) + translate_result(results, error) } /// Executes a query and returns the results as a [Tuple](mentat::QueryResults::Tuple). @@ -1307,18 +1496,14 @@ pub unsafe extern "C" fn query_builder_execute_coll(query_builder: *mut QueryBui /// # 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 `typed_value_list_destroy` is provided for releasing the memory for this /// pointer type. #[no_mangle] -pub unsafe extern "C" fn query_builder_execute_tuple(query_builder: *mut QueryBuilder) -> *mut ExternResult { +pub unsafe extern "C" fn query_builder_execute_tuple(query_builder: *mut QueryBuilder, error: *mut ExternError) -> *mut Vec { + assert_not_null!(query_builder); let query_builder = &mut *query_builder; let results = query_builder.execute_tuple(); - let extern_result = match results { - Ok(Some(v)) => ExternResult { err: std::ptr::null(), ok: Box::into_raw(Box::new(v)) as *const _ as *const c_void, }, - Ok(None) => ExternResult { err: std::ptr::null(), ok: std::ptr::null(), }, - Err(e) => ExternResult { err: string_to_c_char(e.to_string()), ok: std::ptr::null(), } - }; - Box::into_raw(Box::new(extern_result)) + translate_opt_result(results, error) } /// Executes a query and returns the results as a [Rel](mentat::QueryResults::Rel). @@ -1330,13 +1515,14 @@ pub unsafe extern "C" fn query_builder_execute_tuple(query_builder: *mut QueryBu /// # 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 `typed_value_result_set_destroy` is provided for releasing the memory for this /// pointer type. #[no_mangle] -pub unsafe extern "C" fn query_builder_execute(query_builder: *mut QueryBuilder) -> *mut ExternResult { +pub unsafe extern "C" fn query_builder_execute(query_builder: *mut QueryBuilder, error: *mut ExternError) -> *mut RelResult { + assert_not_null!(query_builder); let query_builder = &mut *query_builder; let results = query_builder.execute_rel(); - Box::into_raw(Box::new(results.into())) + translate_result(results, error) } fn unwrap_conversion(value: Option, expected_type: ValueType) -> T { @@ -1355,6 +1541,7 @@ fn unwrap_conversion(value: Option, expected_type: ValueType) -> T { // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] pub unsafe extern "C" fn typed_value_into_long(typed_value: *mut Binding) -> c_longlong { + assert_not_null!(typed_value); let typed_value = Box::from_raw(typed_value); unwrap_conversion(typed_value.into_long(), ValueType::Long) } @@ -1370,6 +1557,7 @@ pub unsafe extern "C" fn typed_value_into_long(typed_value: *mut Binding) -> c_l // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] pub unsafe extern "C" fn typed_value_into_entid(typed_value: *mut Binding) -> Entid { + assert_not_null!(typed_value); let typed_value = Box::from_raw(typed_value); println!("typed value as entid {:?}", typed_value); unwrap_conversion(typed_value.into_entid(), ValueType::Ref) @@ -1383,9 +1571,10 @@ pub unsafe extern "C" fn typed_value_into_entid(typed_value: *mut Binding) -> En /// // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[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) -> *mut c_char { + assert_not_null!(typed_value); let typed_value = Box::from_raw(typed_value); - unwrap_conversion(typed_value.into_kw_c_string(), ValueType::Keyword) as *const c_char + unwrap_conversion(typed_value.into_kw_c_string(), ValueType::Keyword) } /// Consumes a [Binding](mentat::Binding) and returns the value as a boolean represented as an `i32`. @@ -1399,6 +1588,7 @@ pub unsafe extern "C" fn typed_value_into_kw(typed_value: *mut Binding) -> *cons // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] pub unsafe extern "C" fn typed_value_into_boolean(typed_value: *mut Binding) -> i32 { + assert_not_null!(typed_value); let typed_value = Box::from_raw(typed_value); if unwrap_conversion(typed_value.into_boolean(), ValueType::Boolean) { 1 } else { 0 } } @@ -1412,6 +1602,7 @@ pub unsafe extern "C" fn typed_value_into_boolean(typed_value: *mut Binding) -> // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] pub unsafe extern "C" fn typed_value_into_double(typed_value: *mut Binding) -> f64 { + assert_not_null!(typed_value); let typed_value = Box::from_raw(typed_value); unwrap_conversion(typed_value.into_double(), ValueType::Double) } @@ -1425,25 +1616,32 @@ pub unsafe extern "C" fn typed_value_into_double(typed_value: *mut Binding) -> f // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] pub unsafe extern "C" fn typed_value_into_timestamp(typed_value: *mut Binding) -> c_longlong { + assert_not_null!(typed_value); let typed_value = Box::from_raw(typed_value); unwrap_conversion(typed_value.into_timestamp(), ValueType::Instant) } /// Consumes a [Binding](mentat::Binding) and returns the value as a C `String`. /// +/// The caller is responsible for freeing the pointer returned from this function using +/// `rust_c_string_destroy`. +/// /// # Panics /// /// If the [ValueType](mentat::ValueType) of the [Binding](mentat::Binding) is not [ValueType::Long](mentat::ValueType::String). /// // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] -pub unsafe extern "C" fn typed_value_into_string(typed_value: *mut Binding) -> *const c_char { +pub unsafe extern "C" fn typed_value_into_string(typed_value: *mut Binding) -> *mut c_char { + assert_not_null!(typed_value); let typed_value = Box::from_raw(typed_value); - unwrap_conversion(typed_value.into_c_string(), ValueType::String) as *const c_char + unwrap_conversion(typed_value.into_c_string(), ValueType::String) } /// Consumes a [Binding](mentat::Binding) and returns the value as a UUID byte slice of length 16. /// +/// The caller is responsible for freeing the pointer returned from this function using `destroy`. +/// /// # Panics /// /// If the [ValueType](mentat::ValueType) of the [Binding](mentat::Binding) is not [ValueType::Long](mentat::ValueType::Uuid). @@ -1451,6 +1649,7 @@ pub unsafe extern "C" fn typed_value_into_string(typed_value: *mut Binding) -> * // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] pub unsafe extern "C" fn typed_value_into_uuid(typed_value: *mut Binding) -> *mut [u8; 16] { + assert_not_null!(typed_value); let typed_value = Box::from_raw(typed_value); let value = unwrap_conversion(typed_value.into_uuid(), ValueType::Uuid); Box::into_raw(Box::new(*value.as_bytes())) @@ -1473,6 +1672,7 @@ pub unsafe extern "C" fn typed_value_value_type(typed_value: *mut Binding) -> Va /// pointer type. #[no_mangle] pub unsafe extern "C" fn row_at_index(rows: *mut RelResult, index: c_int) -> *mut Vec { + assert_not_null!(rows); let result = &*rows; result.row(index as usize).map_or_else(std::ptr::null_mut, |v| Box::into_raw(Box::new(v.to_vec()))) } @@ -1486,6 +1686,7 @@ pub unsafe extern "C" fn row_at_index(rows: *mut RelResult, index: c_in /// pointer type. #[no_mangle] pub unsafe extern "C" fn typed_value_result_set_into_iter(rows: *mut RelResult) -> *mut BindingListIterator { + assert_not_null!(rows); let result = &*rows; let rows = result.rows(); Box::into_raw(Box::new(rows)) @@ -1501,6 +1702,7 @@ pub unsafe extern "C" fn typed_value_result_set_into_iter(rows: *mut RelResult *mut Vec { + assert_not_null!(iter); let iter = &mut *iter; iter.next().map_or(std::ptr::null_mut(), |v| Box::into_raw(Box::new(v.to_vec()))) } @@ -1514,6 +1716,7 @@ pub unsafe extern "C" fn typed_value_result_set_iter_next(iter: *mut BindingList /// pointer type. #[no_mangle] pub unsafe extern "C" fn typed_value_list_into_iter(values: *mut Vec) -> *mut BindingIterator { + assert_not_null!(values); let result = Box::from_raw(values); Box::into_raw(Box::new(result.into_iter())) } @@ -1528,6 +1731,7 @@ pub unsafe extern "C" fn typed_value_list_into_iter(values: *mut Vec) - /// pointer type. #[no_mangle] pub unsafe extern "C" fn typed_value_list_iter_next(iter: *mut BindingIterator) -> *mut Binding { + assert_not_null!(iter); let iter = &mut *iter; iter.next().map_or(std::ptr::null_mut(), |v| Box::into_raw(Box::new(v))) } @@ -1541,9 +1745,17 @@ pub unsafe extern "C" fn typed_value_list_iter_next(iter: *mut BindingIterator) /// A destructor `typed_value_destroy` is provided for releasing the memory for this /// pointer type. #[no_mangle] -pub unsafe extern "C" fn value_at_index(values: *mut Vec, index: c_int) -> *const Binding { - let result = &*values; - result.get(index as usize).expect("No value at index") as *const Binding +pub unsafe extern "C" fn value_at_index(values: *mut Vec, index: c_int) -> *mut Binding { + assert_not_null!(values); + let values = &*values; + if index < 0 || (index as usize) > values.len() { + std::ptr::null_mut() + } else { + // TODO: an older version of this function returned a reference into values. This + // causes `typed_value_into_*` to be memory unsafe, and goes against the documentation. + // Should there be a version that still behaves in this manner? + Box::into_raw(Box::new(values[index as usize].clone())) + } } /// Returns the value of the [Binding](mentat::Binding) at `index` as a `long`. @@ -1556,6 +1768,7 @@ pub unsafe extern "C" fn value_at_index(values: *mut Vec, index: c_int) // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] pub unsafe extern "C" fn value_at_index_into_long(values: *mut Vec, index: c_int) -> c_longlong { + assert_not_null!(values); let result = &*values; let value = result.get(index as usize).expect("No value at index"); unwrap_conversion(value.clone().into_long(), ValueType::Long) @@ -1571,6 +1784,7 @@ pub unsafe extern "C" fn value_at_index_into_long(values: *mut Vec, ind // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] pub unsafe extern "C" fn value_at_index_into_entid(values: *mut Vec, index: c_int) -> Entid { + assert_not_null!(values); let result = &*values; let value = result.get(index as usize).expect("No value at index"); unwrap_conversion(value.clone().into_entid(), ValueType::Ref) @@ -1585,10 +1799,11 @@ pub unsafe extern "C" fn value_at_index_into_entid(values: *mut Vec, in /// // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] -pub unsafe extern "C" fn value_at_index_into_kw(values: *mut Vec, index: c_int) -> *const c_char { +pub unsafe extern "C" fn value_at_index_into_kw(values: *mut Vec, index: c_int) -> *mut c_char { + assert_not_null!(values); let result = &*values; let value = result.get(index as usize).expect("No value at index"); - unwrap_conversion(value.clone().into_kw_c_string(), ValueType::Keyword) as *const c_char + unwrap_conversion(value.clone().into_kw_c_string(), ValueType::Keyword) as *mut c_char } /// Returns the value of the [Binding](mentat::Binding) at `index` as a boolean represented by a `i32`. @@ -1603,6 +1818,7 @@ pub unsafe extern "C" fn value_at_index_into_kw(values: *mut Vec, index // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] pub unsafe extern "C" fn value_at_index_into_boolean(values: *mut Vec, index: c_int) -> i32 { + assert_not_null!(values); let result = &*values; let value = result.get(index as usize).expect("No value at index"); if unwrap_conversion(value.clone().into_boolean(), ValueType::Boolean) { 1 } else { 0 } @@ -1618,6 +1834,7 @@ pub unsafe extern "C" fn value_at_index_into_boolean(values: *mut Vec, // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] pub unsafe extern "C" fn value_at_index_into_double(values: *mut Vec, index: c_int) -> f64 { + assert_not_null!(values); let result = &*values; let value = result.get(index as usize).expect("No value at index"); unwrap_conversion(value.clone().into_double(), ValueType::Double) @@ -1633,6 +1850,7 @@ pub unsafe extern "C" fn value_at_index_into_double(values: *mut Vec, i // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] pub unsafe extern "C" fn value_at_index_into_timestamp(values: *mut Vec, index: c_int) -> c_longlong { + assert_not_null!(values); let result = &*values; let value = result.get(index as usize).expect("No value at index"); unwrap_conversion(value.clone().into_timestamp(), ValueType::Instant) @@ -1647,10 +1865,11 @@ pub unsafe extern "C" fn value_at_index_into_timestamp(values: *mut Vec /// // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] -pub unsafe extern "C" fn value_at_index_into_string(values: *mut Vec, index: c_int) -> *const c_char { +pub unsafe extern "C" fn value_at_index_into_string(values: *mut Vec, index: c_int) -> *mut c_char { + assert_not_null!(values); let result = &*values; let value = result.get(index as usize).expect("No value at index"); - unwrap_conversion(value.clone().into_c_string(), ValueType::String) as *const c_char + unwrap_conversion(value.clone().into_c_string(), ValueType::String) as *mut c_char } /// Returns the value of the [Binding](mentat::Binding) at `index` as a UUID byte slice of length 16. @@ -1663,39 +1882,35 @@ pub unsafe extern "C" fn value_at_index_into_string(values: *mut Vec, i // TODO Generalise with macro https://github.com/mozilla/mentat/issues/703 #[no_mangle] pub unsafe extern "C" fn value_at_index_into_uuid(values: *mut Vec, index: c_int) -> *mut [u8; 16] { + assert_not_null!(values); let result = &*values; let value = result.get(index as usize).expect("No value at index"); let uuid = unwrap_conversion(value.clone().into_uuid(), ValueType::Uuid); Box::into_raw(Box::new(*uuid.as_bytes())) } -/// Returns an [ExternResult](ExternResult) containing the [Binding](mentat::Binding) associated with the `attribute` as `:namespace/name` -/// for the given `entid`. -/// If there is a value for that `attribute` on the entity with id `entid` then the value is returned in `ok`. +/// Returns a pointer to the the [Binding](mentat::Binding) associated with the `attribute` as +/// `:namespace/name` for the given `entid`. +/// If there is a value for that `attribute` on the entity with id `entid` then the value is returned. /// If there no value for that `attribute` on the entity with id `entid` but the attribute is value, -/// then a null pointer is returned in `ok`. -/// If there is no [Attribute](mentat::Attribute) in the [Schema](mentat::Schema) for the given `attribute` then an error is returned in `err`. +/// then a null pointer is returned. +/// If there is no [Attribute](mentat::Attribute) in the [Schema](mentat::Schema) for the given +/// `attribute` then a null pointer is returned and an error is stored in is returned in `error`, /// /// # 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 `typed_value_destroy` is provided for releasing the memory for this /// pointer type. /// /// TODO: list the types of error that can be caused by this function #[no_mangle] -pub unsafe extern "C" fn store_value_for_attribute(store: *mut Store, entid: c_longlong, attribute: *const c_char) -> *mut ExternResult { +pub unsafe extern "C" fn store_value_for_attribute(store: *mut Store, entid: c_longlong, attribute: *const c_char, error: *mut ExternError) -> *mut Binding { + assert_not_null!(store); let store = &*store; let kw = kw_from_string(c_char_to_string(attribute)); - let value = match store.lookup_value_for_attribute(entid, &kw) { - Ok(Some(v)) => { - let value: Binding = v.into(); - ExternResult { ok: Box::into_raw(Box::new(value)) as *const _ as *const c_void, err: std::ptr::null() } - }, - Ok(None) => ExternResult { ok: std::ptr::null(), err: std::ptr::null() }, - Err(e) => ExternResult { ok: std::ptr::null(), err: string_to_c_char(e.to_string()) }, - }; - Box::into_raw(Box::new(value)) + let result = store.lookup_value_for_attribute(entid, &kw).map(|o| o.map(Binding::from)); + translate_opt_result(result, error) } /// Registers a [TxObserver](mentat::TxObserver) with the `key` to observe changes to `attributes` @@ -1712,27 +1927,31 @@ pub unsafe extern "C" fn store_register_observer(store: *mut Store, attributes: *const Entid, attributes_len: usize, callback: extern fn(key: *const c_char, reports: &TxChangeList)) { + assert_not_null!(store, attributes); let store = &mut *store; let mut attribute_set = BTreeSet::new(); let slice = slice::from_raw_parts(attributes, attributes_len); attribute_set.extend(slice.iter()); let key = c_char_to_string(key); let tx_observer = Arc::new(TxObserver::new(attribute_set, move |obs_key, batch| { - let extern_reports: Vec = batch.into_iter().map(|(tx_id, changes)| { - let changes: Vec = changes.into_iter().map(|i|*i).collect(); - let len = changes.len(); - TransactionChange { - txid: *tx_id, - changes: changes.into_boxed_slice(), - changes_len: len, - } + let reports: Vec<(Entid, Vec)> = batch.into_iter().map(|(tx_id, changes)| { + (*tx_id, changes.into_iter().map(|eid| *eid as c_longlong).collect()) }).collect(); + let extern_reports = reports.iter().map(|item| { + TransactionChange { + txid: item.0, + changes: item.1.as_ptr(), + changes_len: item.1.len() as c_ulonglong + } + }).collect::>(); let len = extern_reports.len(); - let reports = TxChangeList { - reports: extern_reports.into_boxed_slice(), - len: len, + let change_list = TxChangeList { + reports: extern_reports.as_ptr(), + len: len as c_ulonglong, }; - callback(string_to_c_char(obs_key), &reports); + let s = string_to_c_char(obs_key); + callback(s, &change_list); + rust_c_string_destroy(s); })); store.register_observer(key.to_string(), tx_observer); } @@ -1740,6 +1959,7 @@ pub unsafe extern "C" fn store_register_observer(store: *mut Store, /// Unregisters a [TxObserver](mentat::TxObserver) with the `key` to observe changes on this `store`. #[no_mangle] pub unsafe extern "C" fn store_unregister_observer(store: *mut Store, key: *const c_char) { + assert_not_null!(store); let store = &mut *store; let key = c_char_to_string(key).to_string(); store.unregister_observer(&key); @@ -1752,6 +1972,7 @@ pub unsafe extern "C" fn store_unregister_observer(store: *mut Store, key: *cons /// If there is no [Attribute](mentat::Attribute) in the [Schema](mentat::Schema) for `attr`. #[no_mangle] pub unsafe extern "C" fn store_entid_for_attribute(store: *mut Store, attr: *const c_char) -> Entid { + assert_not_null!(store); let store = &mut *store; let keyword_string = c_char_to_string(attr); let kw = kw_from_string(keyword_string); @@ -1766,17 +1987,12 @@ pub unsafe extern "C" fn store_entid_for_attribute(store: *mut Store, attr: *con /// /// If there is no value present at the `index`. /// -/// # Safety -/// -/// Callers are responsible for managing the memory for the return value. -/// A destructor `typed_value_destroy` is provided for releasing the memory for this -/// pointer type. #[no_mangle] pub unsafe extern "C" fn tx_change_list_entry_at(tx_report_list: *mut TxChangeList, index: c_int) -> *const TransactionChange { + assert_not_null!(tx_report_list); let tx_report_list = &*tx_report_list; - let index = index as usize; - let report = Box::new(tx_report_list.reports[index].clone()); - Box::into_raw(report) + assert!(0 <= index && (index as usize) < (tx_report_list.len as usize)); + tx_report_list.reports.offset(index as isize) } /// Returns the value at the provided `index` as a [Entid](mentat::Entid) . @@ -1786,9 +2002,17 @@ pub unsafe extern "C" fn tx_change_list_entry_at(tx_report_list: *mut TxChangeLi /// If there is no value present at the `index`. #[no_mangle] pub unsafe extern "C" fn changelist_entry_at(tx_report: *mut TransactionChange, index: c_int) -> Entid { + assert_not_null!(tx_report); let tx_report = &*tx_report; - let index = index as usize; - tx_report.changes[index].clone() + assert!(0 <= index && (index as usize) < (tx_report.changes_len as usize)); + std::ptr::read(tx_report.changes.offset(index as isize)) +} + +#[no_mangle] +pub unsafe extern "C" fn rust_c_string_destroy(s: *mut c_char) { + if !s.is_null() { + let _ = CString::from_raw(s); + } } /// Creates a function with a given `$name` that releases the memory for a type `$t`. @@ -1796,7 +2020,9 @@ macro_rules! define_destructor ( ($name:ident, $t:ty) => ( #[no_mangle] pub unsafe extern "C" fn $name(obj: *mut $t) { - let _ = Box::from_raw(obj); + if !obj.is_null() { + let _ = Box::from_raw(obj); + } } ) ); @@ -1812,7 +2038,9 @@ macro_rules! define_destructor_with_lifetimes ( ($name:ident, $t:ty) => ( #[no_mangle] pub unsafe extern "C" fn $name<'a, 'c>(obj: *mut $t) { - let _ = Box::from_raw(obj); + if !obj.is_null() { + let _ = Box::from_raw(obj); + } } ) ); @@ -1820,6 +2048,9 @@ macro_rules! define_destructor_with_lifetimes ( /// destroy function for releasing the memory for `repr(C)` structs. define_destructor!(destroy, c_void); +/// destroy function for releasing the memory of UUIDs +define_destructor!(uuid_destroy, [u8; 16]); + /// Destructor for releasing the memory of [InProgressBuilder](mentat::InProgressBuilder). define_destructor_with_lifetimes!(in_progress_builder_destroy, InProgressBuilder<'a, 'c>); diff --git a/ffi/src/utils.rs b/ffi/src/utils.rs index 246ef4a8..ed805aa4 100644 --- a/ffi/src/utils.rs +++ b/ffi/src/utils.rs @@ -20,6 +20,7 @@ pub mod strings { }; pub fn c_char_to_string(cchar: *const c_char) -> &'static str { + assert!(!cchar.is_null()); let c_str = unsafe { CStr::from_ptr(cchar) }; c_str.to_str().unwrap_or("") } @@ -61,3 +62,96 @@ pub mod log { unsafe { android::__android_log_write(android::LogLevel::Debug as i32, tag, message) }; } } + +pub mod error { + use super::strings::string_to_c_char; + use std::os::raw::c_char; + use std::boxed::Box; + use std::fmt::Display; + use std::ptr; + + /// Represents an error that occurred on the mentat side. Many mentat FFI functions take a + /// `*mut ExternError` as the last argument. This is an out parameter that indicates an + /// error that occurred during that function's execution (if any). + /// + /// For functions that use this pattern, if the ExternError's message property is null, then no + /// error occurred. If the message is non-null then it contains a string description of the + /// error that occurred. + /// + /// Important: This message is allocated on the heap and it is the consumer's responsibility to + /// free it using `destroy_mentat_string`! + /// + /// While this pattern is not ergonomic in Rust, it offers two main benefits: + /// + /// 1. It avoids defining a large number of `Result`-shaped types in the FFI consumer, as would + /// be required with something like an `struct ExternResult { ok: *mut T, err:... }` + /// 2. It offers additional type safety over `struct ExternResult { ok: *mut c_void, err:... }`, + /// which helps avoid memory safety errors. + #[repr(C)] + #[derive(Debug)] + pub struct ExternError { + pub message: *mut c_char, + // TODO: Include an error code here. + } + + impl Default for ExternError { + fn default() -> ExternError { + ExternError { message: ptr::null_mut() } + } + } + + /// Translate Result, into something C can understand, when T is not `#[repr(C)]` + /// + /// - If `result` is `Ok(v)`, moves `v` to the heap and returns a pointer to it, and sets + /// `error` to a state indicating that no error occurred (`message` is null). + /// - If `result` is `Err(e)`, returns a null pointer and stores a string representing the error + /// message (which was allocated on the heap and should eventually be freed) into + /// `error.message` + pub unsafe fn translate_result(result: Result, error: *mut ExternError) -> *mut T + where E: Display { + // TODO: can't unwind across FFI... + assert!(!error.is_null(), "Error output parameter is not optional"); + let error = &mut *error; + error.message = ptr::null_mut(); + match result { + Ok(val) => Box::into_raw(Box::new(val)), + Err(e) => { + error.message = string_to_c_char(e.to_string()); + ptr::null_mut() + } + } + } + + /// Translate Result, E> into something C can understand, when T is not `#[repr(C)]`. + /// + /// - If `result` is `Ok(Some(v))`, moves `v` to the heap and returns a pointer to it, and + /// sets `error` to a state indicating that no error occurred (`message` is null). + /// - If `result` is `Ok(None)` returns a null pointer, but sets `error` to a state indicating + /// that no error occurred (`message` is null). + /// - If `result` is `Err(e)`, returns a null pointer and stores a string representing the error + /// message (which was allocated on the heap and should eventually be freed) into + /// `error.message` + pub unsafe fn translate_opt_result(result: Result, E>, error: *mut ExternError) -> *mut T + where E: Display { + assert!(!error.is_null(), "Error output parameter is not optional"); + let error = &mut *error; + error.message = ptr::null_mut(); + match result { + Ok(Some(val)) => Box::into_raw(Box::new(val)), + Ok(None) => ptr::null_mut(), + Err(e) => { + error.message = string_to_c_char(e.to_string()); + ptr::null_mut() + } + } + } + + /// Identical to `translate_result`, but with additional type checking for the case that we have + /// a `Result<(), E>` (which we're about to drop on the floor). + pub unsafe fn translate_void_result(result: Result<(), E>, error: *mut ExternError) where E: Display { + // Note that Box guarantees that if T is zero sized, it's not heap allocated. So not + // only do we never need to free the return value of this, it would be a problem if someone did. + translate_result(result, error); + } +} + diff --git a/scripts/ios_build.sh b/scripts/ios_build.sh index 95224319..468e8ebb 100755 --- a/scripts/ios_build.sh +++ b/scripts/ios_build.sh @@ -1,4 +1,5 @@ cd ffi cargo lipo --release cd .. +mkdir -p sdks/swift/Mentat/External-Dependencies/ cp target/universal/release/libmentat_ffi.a sdks/swift/Mentat/External-Dependencies/ diff --git a/sdks/android/Mentat/build.gradle b/sdks/android/Mentat/build.gradle index 8146fd8e..ee3ebc49 100644 --- a/sdks/android/Mentat/build.gradle +++ b/sdks/android/Mentat/build.gradle @@ -10,7 +10,7 @@ buildscript { ext.build = [ compileSdkVersion: 27, targetSdkVersion: 27, - minSdkVersion: 16 + minSdkVersion: 19 ] repositories { diff --git a/sdks/android/Mentat/library/src/androidTest/java/org/mozilla/mentat/FFIIntegrationTest.java b/sdks/android/Mentat/library/src/androidTest/java/org/mozilla/mentat/FFIIntegrationTest.java index ebcc982a..ac199eb6 100644 --- a/sdks/android/Mentat/library/src/androidTest/java/org/mozilla/mentat/FFIIntegrationTest.java +++ b/sdks/android/Mentat/library/src/androidTest/java/org/mozilla/mentat/FFIIntegrationTest.java @@ -1016,10 +1016,11 @@ public class FFIIntegrationTest { builder.addRef(bEntid, ":foo/ref", longEntid); InProgressTransactionResult result = builder.transact(); assertNotNull(result); - assertNotNull(result.getInProgress()); + InProgress inProgress = result.getInProgress(); + assertNotNull(inProgress); assertNotNull(result.getReport()); - result.getInProgress().transact("[[:db/add "+ aEntid +" :foo/long 22]]"); - result.getInProgress().commit(); + inProgress.transact("[[:db/add "+ aEntid +" :foo/long 22]]"); + inProgress.commit(); // test that the values are as expected String query = "[:find [?b ?i ?u ?l ?d ?s ?k ?r]\n" + @@ -1081,10 +1082,11 @@ public class FFIIntegrationTest { builder.addRef(":foo/ref", longEntid); InProgressTransactionResult result = builder.transact(); assertNotNull(result); - assertNotNull(result.getInProgress()); + InProgress inProgress = result.getInProgress(); + assertNotNull(inProgress); assertNotNull(result.getReport()); - result.getInProgress().transact("[[:db/add "+ aEntid +" :foo/long 22]]"); - result.getInProgress().commit(); + inProgress.transact("[[:db/add "+ aEntid +" :foo/long 22]]"); + inProgress.commit(); // test that the values are as expected String query = "[:find [?b ?i ?u ?l ?d ?s ?k ?r]\n" + diff --git a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/AttributeList.java b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/AttributeList.java deleted file mode 100644 index 5b3255a3..00000000 --- a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/AttributeList.java +++ /dev/null @@ -1,48 +0,0 @@ -/* -*- 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 org.mozilla.mentat; - -import com.sun.jna.Structure; -import com.sun.jna.ptr.IntByReference; - -import java.io.Closeable; -import java.util.Arrays; -import java.util.List; - -/** - * Represents a C struct of a list of Strings containing attributes in the format - * `:namespace/name`. - */ -public class AttributeList extends Structure implements Closeable { - public static class ByReference extends AttributeList implements Structure.ByReference { - } - - public static class ByValue extends AttributeList implements Structure.ByValue { - } - - public IntByReference attributes; - public int numberOfItems; - // Used by the Swift counterpart, JNA does this for us automagically. - // But we still need it here so that the number of fields and their order is correct - public int len; - - @Override - protected List getFieldOrder() { - return Arrays.asList("attributes", "numberOfItems", "len"); - } - - @Override - public void close() { - if (this.getPointer() != null) { - JNA.INSTANCE.destroy(this.getPointer()); - } - } -} diff --git a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/ColResultIterator.java b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/ColResultIterator.java index 2e2fd3df..13917ed6 100644 --- a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/ColResultIterator.java +++ b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/ColResultIterator.java @@ -12,43 +12,27 @@ package org.mozilla.mentat; import com.sun.jna.Pointer; -import java.util.Iterator; - /** * Iterator for a {@link CollResult} */ -public class ColResultIterator extends RustObject implements Iterator { +public class ColResultIterator extends RustIterator { - Pointer nextPointer; - - ColResultIterator(Pointer iterator) { - this.rawPointer = iterator; - } - - private Pointer getNextPointer() { - return JNA.INSTANCE.typed_value_list_iter_next(this.rawPointer); + ColResultIterator(JNA.TypedValueListIter iterator) { + super(iterator); } @Override - public boolean hasNext() { - this.nextPointer = getNextPointer(); - return this.nextPointer != null; + protected JNA.TypedValue advanceIterator() { + return JNA.INSTANCE.typed_value_list_iter_next(this.validPointer()); } @Override - public TypedValue next() { - Pointer next = this.nextPointer == null ? getNextPointer() : this.nextPointer; - if (next == null) { - return null; - } - - return new TypedValue(next); + protected TypedValue constructItem(JNA.TypedValue p) { + return new TypedValue(p); } @Override - public void close() { - if (this.rawPointer != null) { - JNA.INSTANCE.typed_value_list_iter_destroy(this.rawPointer); - } + protected void destroyPointer(JNA.TypedValueListIter p) { + JNA.INSTANCE.typed_value_list_iter_destroy(p); } } diff --git a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/CollResult.java b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/CollResult.java index 8baf61ef..f2bf4370 100644 --- a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/CollResult.java +++ b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/CollResult.java @@ -37,21 +37,13 @@ import java.util.UUID; */ public class CollResult extends TupleResult implements Iterable { - public CollResult(Pointer pointer) { + public CollResult(JNA.TypedValueList pointer) { super(pointer); } - @Override - public void close() { - if (this.rawPointer != null) { - JNA.INSTANCE.destroy(this.rawPointer); - } - } - @Override public ColResultIterator iterator() { - Pointer iterPointer = JNA.INSTANCE.typed_value_list_into_iter(this.rawPointer); - this.rawPointer = null; + JNA.TypedValueListIter iterPointer = JNA.INSTANCE.typed_value_list_into_iter(this.consumePointer()); if (iterPointer == null) { return null; } diff --git a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/EntityBuilder.java b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/EntityBuilder.java index 776afe29..fe565f28 100644 --- a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/EntityBuilder.java +++ b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/EntityBuilder.java @@ -14,7 +14,6 @@ import android.util.Log; import com.sun.jna.Pointer; -import java.io.IOException; import java.util.Date; import java.util.UUID; @@ -54,10 +53,10 @@ import java.util.UUID; * builder.commit(); * } */ -public class EntityBuilder extends RustObject { +public class EntityBuilder extends RustObject { - public EntityBuilder(Pointer pointer) { - this.rawPointer = pointer; + public EntityBuilder(JNA.EntityBuilder pointer) { + super(pointer); } /** @@ -66,11 +65,9 @@ public class EntityBuilder extends RustObject { * @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); - } + RustError.ByReference err = new RustError.ByReference(); + JNA.INSTANCE.entity_builder_add_long(this.validPointer(), keyword, value, err); + err.logAndConsumeError("EntityBuilder"); } /** @@ -80,11 +77,9 @@ public class EntityBuilder extends RustObject { * @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); - } + RustError.ByReference err = new RustError.ByReference(); + JNA.INSTANCE.entity_builder_add_ref(this.validPointer(), keyword, value, err); + err.logAndConsumeError("EntityBuilder"); } /** @@ -94,11 +89,9 @@ public class EntityBuilder extends RustObject { * @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); - } + RustError.ByReference err = new RustError.ByReference(); + JNA.INSTANCE.entity_builder_add_keyword(this.validPointer(), keyword, value, err); + err.logAndConsumeError("EntityBuilder"); } /** @@ -108,11 +101,9 @@ public class EntityBuilder extends RustObject { * @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); - } + RustError.ByReference err = new RustError.ByReference(); + JNA.INSTANCE.entity_builder_add_boolean(this.validPointer(), keyword, value ? 1 : 0, err); + err.logAndConsumeError("EntityBuilder"); } /** @@ -122,11 +113,9 @@ public class EntityBuilder extends RustObject { * @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); - } + RustError.ByReference err = new RustError.ByReference(); + JNA.INSTANCE.entity_builder_add_double(this.validPointer(), keyword, value, err); + err.logAndConsumeError("EntityBuilder"); } /** @@ -136,11 +125,9 @@ public class EntityBuilder extends RustObject { * @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); - } + RustError.ByReference err = new RustError.ByReference(); + JNA.INSTANCE.entity_builder_add_timestamp(this.validPointer(), keyword, value.getTime() * 1_000, err); + err.logAndConsumeError("EntityBuilder"); } /** @@ -150,11 +137,9 @@ public class EntityBuilder extends RustObject { * @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); - } + RustError.ByReference err = new RustError.ByReference(); + JNA.INSTANCE.entity_builder_add_string(this.validPointer(), keyword, value, err); + err.logAndConsumeError("EntityBuilder"); } /** @@ -164,12 +149,9 @@ public class EntityBuilder extends RustObject { * @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); - } + RustError.ByReference err = new RustError.ByReference(); + JNA.INSTANCE.entity_builder_add_uuid(this.validPointer(), keyword, getPointerForUUID(value), err); + err.logAndConsumeError("EntityBuilder"); } /** @@ -181,11 +163,9 @@ public class EntityBuilder extends RustObject { * @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); - } + RustError.ByReference err = new RustError.ByReference(); + JNA.INSTANCE.entity_builder_retract_long(this.validPointer(), keyword, value, err); + err.logAndConsumeError("EntityBuilder"); } @@ -198,11 +178,9 @@ public class EntityBuilder extends RustObject { * @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); - } + RustError.ByReference err = new RustError.ByReference(); + JNA.INSTANCE.entity_builder_retract_ref(this.validPointer(), keyword, value, err); + err.logAndConsumeError("EntityBuilder"); } /** @@ -214,11 +192,9 @@ public class EntityBuilder extends RustObject { * @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); - } + RustError.ByReference err = new RustError.ByReference(); + JNA.INSTANCE.entity_builder_retract_keyword(this.validPointer(), keyword, value, err); + err.logAndConsumeError("EntityBuilder"); } /** @@ -230,11 +206,9 @@ public class EntityBuilder extends RustObject { * @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); - } + RustError.ByReference err = new RustError.ByReference(); + JNA.INSTANCE.entity_builder_retract_boolean(this.validPointer(), keyword, value ? 1 : 0, err); + err.logAndConsumeError("EntityBuilder"); } /** @@ -246,11 +220,9 @@ public class EntityBuilder extends RustObject { * @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); - } + RustError.ByReference err = new RustError.ByReference(); + JNA.INSTANCE.entity_builder_retract_double(this.validPointer(), keyword, value, err); + err.logAndConsumeError("EntityBuilder"); } /** @@ -262,11 +234,9 @@ public class EntityBuilder extends RustObject { * @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); - } + RustError.ByReference err = new RustError.ByReference(); + JNA.INSTANCE.entity_builder_retract_timestamp(this.validPointer(), keyword, value.getTime() * 1_000, err); + err.logAndConsumeError("EntityBuilder"); } /** @@ -278,11 +248,9 @@ public class EntityBuilder extends RustObject { * @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); - } + RustError.ByReference err = new RustError.ByReference(); + JNA.INSTANCE.entity_builder_retract_string(this.validPointer(), keyword, value, err); + err.logAndConsumeError("EntityBuilder"); } /** @@ -294,11 +262,9 @@ public class EntityBuilder extends RustObject { * @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); - } + RustError.ByReference err = new RustError.ByReference(); + JNA.INSTANCE.entity_builder_retract_uuid(this.validPointer(), keyword, getPointerForUUID(value), err); + err.logAndConsumeError("EntityBuilder"); } /** @@ -316,10 +282,7 @@ public class EntityBuilder extends RustObject { * 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; + return JNA.INSTANCE.entity_builder_transact(this.consumePointer()); } /** @@ -335,21 +298,17 @@ public class EntityBuilder extends RustObject { * @return The {@link TxReport} generated by the commit. */ 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); + RustError.ByReference err = new RustError.ByReference(); + JNA.TxReport report = JNA.INSTANCE.entity_builder_commit(this.consumePointer(), err); + if (err.isFailure()) { + Log.e("EntityBuilder", err.consumeErrorMessage()); return null; } - - return new TxReport(result.ok); + return new TxReport(report); } @Override - public void close() throws IOException { - if (this.rawPointer != null) { - JNA.INSTANCE.entity_builder_destroy(this.rawPointer); - } + protected void destroyPointer(JNA.EntityBuilder p) { + JNA.INSTANCE.entity_builder_destroy(p); } } diff --git a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/InProgress.java b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/InProgress.java index 6130706b..e0e82172 100644 --- a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/InProgress.java +++ b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/InProgress.java @@ -14,8 +14,6 @@ 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. *

@@ -77,10 +75,10 @@ import java.io.IOException; * inProgress.commit(); * } */ -public class InProgress extends RustObject { +public class InProgress extends RustObject { - public InProgress(Pointer pointer) { - this.rawPointer = pointer; + public InProgress(JNA.InProgress pointer) { + super(pointer); } /** @@ -89,10 +87,7 @@ public class InProgress extends RustObject { * @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; + return new InProgressBuilder(JNA.INSTANCE.in_progress_builder(this.consumePointer())); } /** @@ -102,11 +97,8 @@ public class InProgress extends RustObject { * @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; + public EntityBuilder builderForEntid(long entid) { + return new EntityBuilder(JNA.INSTANCE.in_progress_entity_builder_from_entid(this.consumePointer(), entid)); } /** @@ -117,10 +109,7 @@ public class InProgress extends RustObject { * @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; + return new EntityBuilder(JNA.INSTANCE.in_progress_entity_builder_from_temp_id(this.consumePointer(), tempid)); } /** @@ -146,13 +135,13 @@ public class InProgress extends RustObject { * @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); + RustError.ByReference err = new RustError.ByReference(); + JNA.TxReport txr = JNA.INSTANCE.in_progress_transact(this.validPointer(), transaction, err); + if (err.isFailure()) { + Log.e("InProgress", err.consumeErrorMessage()); return null; } - return new TxReport(result.ok); + return new TxReport(txr); } /** @@ -163,12 +152,9 @@ public class InProgress extends RustObject { * 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); - } + RustError.ByReference err = new RustError.ByReference(); + JNA.INSTANCE.in_progress_commit(this.consumePointer(), err); + err.logAndConsumeError("InProgress"); } /** @@ -178,18 +164,13 @@ public class InProgress extends RustObject { * 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); - } + RustError.ByReference err = new RustError.ByReference(); + JNA.INSTANCE.in_progress_rollback(this.consumePointer(), err); + err.logAndConsumeError("InProgress"); } @Override - public void close() throws IOException { - if (this.rawPointer != null) { - JNA.INSTANCE.in_progress_destroy(this.rawPointer); - } + protected void destroyPointer(JNA.InProgress p) { + JNA.INSTANCE.in_progress_destroy(p); } } diff --git a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/InProgressBuilder.java b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/InProgressBuilder.java index f23effe0..4aaba924 100644 --- a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/InProgressBuilder.java +++ b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/InProgressBuilder.java @@ -14,7 +14,6 @@ import android.util.Log; import com.sun.jna.Pointer; -import java.io.IOException; import java.util.Date; import java.util.UUID; @@ -56,10 +55,10 @@ import java.util.UUID; * builder.commit(); * } */ -public class InProgressBuilder extends RustObject { +public class InProgressBuilder extends RustObject { - public InProgressBuilder(Pointer pointer) { - this.rawPointer = pointer; + public InProgressBuilder(JNA.InProgressBuilder pointer) { + super(pointer); } /** * Asserts the value of attribute `keyword` to be the provided `value`. @@ -71,11 +70,9 @@ public class InProgressBuilder extends RustObject { * @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); - } + RustError.ByReference error = new RustError.ByReference(); + JNA.INSTANCE.in_progress_builder_add_long(this.validPointer(), entid, keyword, value, error); + error.logAndConsumeError("InProgressBuilder"); } /** @@ -87,11 +84,9 @@ public class InProgressBuilder extends RustObject { * @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); - } + RustError.ByReference error = new RustError.ByReference(); + JNA.INSTANCE.in_progress_builder_add_ref(this.validPointer(), entid, keyword, value, error); + error.logAndConsumeError("InProgressBuilder"); } /** @@ -103,11 +98,9 @@ public class InProgressBuilder extends RustObject { * @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); - } + RustError.ByReference error = new RustError.ByReference(); + JNA.INSTANCE.in_progress_builder_add_keyword(this.validPointer(), entid, keyword, value, error); + error.logAndConsumeError("InProgressBuilder"); } /** @@ -119,11 +112,9 @@ public class InProgressBuilder extends RustObject { * @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); - } + RustError.ByReference error = new RustError.ByReference(); + JNA.INSTANCE.in_progress_builder_add_boolean(this.validPointer(), entid, keyword, value ? 1 : 0, error); + error.logAndConsumeError("InProgressBuilder"); } /** @@ -135,11 +126,9 @@ public class InProgressBuilder extends RustObject { * @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); - } + RustError.ByReference error = new RustError.ByReference(); + JNA.INSTANCE.in_progress_builder_add_double(this.validPointer(), entid, keyword, value, error); + error.logAndConsumeError("InProgressBuilder"); } /** @@ -151,11 +140,9 @@ public class InProgressBuilder extends RustObject { * @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); - } + RustError.ByReference error = new RustError.ByReference(); + JNA.INSTANCE.in_progress_builder_add_timestamp(this.validPointer(), entid, keyword, value.getTime() * 1_000, error); + error.logAndConsumeError("InProgressBuilder"); } /** @@ -167,11 +154,9 @@ public class InProgressBuilder extends RustObject { * @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); - } + RustError.ByReference error = new RustError.ByReference(); + JNA.INSTANCE.in_progress_builder_add_string(this.validPointer(), entid, keyword, value, error); + error.logAndConsumeError("InProgressBuilder"); } /** @@ -183,12 +168,9 @@ public class InProgressBuilder extends RustObject { * @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); - } + RustError.ByReference error = new RustError.ByReference(); + JNA.INSTANCE.in_progress_builder_add_uuid(this.validPointer(), entid, keyword, getPointerForUUID(value), error); + error.logAndConsumeError("InProgressBuilder"); } /** @@ -201,11 +183,9 @@ public class InProgressBuilder extends RustObject { * @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); - } + RustError.ByReference error = new RustError.ByReference(); + JNA.INSTANCE.in_progress_builder_retract_long(this.validPointer(), entid, keyword, value, error); + error.logAndConsumeError("InProgressBuilder"); } @@ -219,11 +199,9 @@ public class InProgressBuilder extends RustObject { * @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); - } + RustError.ByReference error = new RustError.ByReference(); + JNA.INSTANCE.in_progress_builder_retract_ref(this.validPointer(), entid, keyword, value, error); + error.logAndConsumeError("InProgressBuilder"); } /** @@ -236,11 +214,9 @@ public class InProgressBuilder extends RustObject { * @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); - } + RustError.ByReference error = new RustError.ByReference(); + JNA.INSTANCE.in_progress_builder_retract_keyword(this.validPointer(), entid, keyword, value, error); + error.logAndConsumeError("InProgressBuilder"); } /** @@ -253,11 +229,9 @@ public class InProgressBuilder extends RustObject { * @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); - } + RustError.ByReference error = new RustError.ByReference(); + JNA.INSTANCE.in_progress_builder_retract_boolean(this.validPointer(), entid, keyword, value ? 1 : 0, error); + error.logAndConsumeError("InProgressBuilder"); } /** @@ -270,11 +244,9 @@ public class InProgressBuilder extends RustObject { * @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); - } + RustError.ByReference error = new RustError.ByReference(); + JNA.INSTANCE.in_progress_builder_retract_double(this.validPointer(), entid, keyword, value, error); + error.logAndConsumeError("InProgressBuilder"); } /** @@ -287,11 +259,9 @@ public class InProgressBuilder extends RustObject { * @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); - } + RustError.ByReference error = new RustError.ByReference(); + JNA.INSTANCE.in_progress_builder_retract_timestamp(this.validPointer(), entid, keyword, value.getTime() * 1_000, error); + error.logAndConsumeError("InProgressBuilder"); } /** @@ -304,11 +274,9 @@ public class InProgressBuilder extends RustObject { * @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); - } + RustError.ByReference error = new RustError.ByReference(); + JNA.INSTANCE.in_progress_builder_retract_string(this.validPointer(), entid, keyword, value, error); + error.logAndConsumeError("InProgressBuilder"); } /** @@ -321,11 +289,9 @@ public class InProgressBuilder extends RustObject { * @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); - } + RustError.ByReference error = new RustError.ByReference(); + JNA.INSTANCE.in_progress_builder_retract_uuid(this.validPointer(), entid, keyword, this.getPointerForUUID(value), error); + error.logAndConsumeError("InProgressBuilder"); } /** @@ -343,9 +309,7 @@ public class InProgressBuilder extends RustObject { * 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; + InProgressTransactionResult result = JNA.INSTANCE.in_progress_builder_transact(this.consumePointer()); return result; } @@ -362,21 +326,17 @@ public class InProgressBuilder extends RustObject { * @return The {@link TxReport} generated by the commit. */ 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); + RustError.ByReference error = new RustError.ByReference(); + JNA.TxReport result = JNA.INSTANCE.in_progress_builder_commit(this.consumePointer(), error); + if (error.isFailure()) { + Log.e("InProgressBuilder", error.consumeErrorMessage()); return null; } - - return new TxReport(result.ok); + return new TxReport(result); } @Override - public void close() throws IOException { - if (this.rawPointer != null) { - JNA.INSTANCE.in_progress_builder_destroy(this.rawPointer); - } + protected void destroyPointer(JNA.InProgressBuilder p) { + JNA.INSTANCE.in_progress_builder_destroy(p); } } diff --git a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/InProgressTransactionResult.java b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/InProgressTransactionResult.java index 6148d16e..a58bda5e 100644 --- a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/InProgressTransactionResult.java +++ b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/InProgressTransactionResult.java @@ -15,43 +15,57 @@ 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 class InProgressTransactionResult extends Structure { 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; + public JNA.InProgress inProgress; + public JNA.TxReport txReport; + public RustError error; @Override protected List getFieldOrder() { - return Arrays.asList("inProgress", "result"); + return Arrays.asList("inProgress", "txReport", "error"); } public InProgress getInProgress() { - return new InProgress(this.inProgress); + if (this.inProgress == null) { + throw new NullPointerException("Already consumed InProgress"); + } + InProgress ip = new InProgress(this.inProgress); + this.inProgress = null; + return ip; + } - + public TxReport getReport() { - if (this.result.isFailure()) { - Log.e("InProgressTransactRes", this.result.err); + if (this.error.isFailure()) { + Log.e("InProgressTransactRes", this.error.consumeErrorMessage()); return null; } - - return new TxReport(this.result.ok); + if (this.txReport == null) { + throw new NullPointerException("Already consumed TxReport"); + } + JNA.TxReport report = this.txReport; + this.txReport = null; + return new TxReport(report); } @Override - public void close() throws IOException { - if (this.getPointer() != null) { - JNA.INSTANCE.destroy(this.getPointer()); + protected void finalize() { + if (this.txReport != null) { + JNA.INSTANCE.tx_report_destroy(this.txReport); + this.txReport = null; + } + if (this.inProgress != null) { + JNA.INSTANCE.in_progress_destroy(this.inProgress); + this.inProgress = null; } } } diff --git a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/JNA.java b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/JNA.java index 1b3b0023..b8ba5d42 100644 --- a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/JNA.java +++ b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/JNA.java @@ -14,6 +14,9 @@ import com.sun.jna.Library; import com.sun.jna.Native; import com.sun.jna.NativeLibrary; import com.sun.jna.Pointer; +import com.sun.jna.PointerType; + +import java.lang.reflect.Type; /** * JNA interface for FFI to Mentat's Rust library @@ -26,136 +29,148 @@ public interface JNA extends Library { JNA INSTANCE = (JNA) Native.loadLibrary(JNA_LIBRARY_NAME, JNA.class); - Pointer store_open(String dbPath); + + class Store extends PointerType {} + class QueryBuilder extends PointerType {} + class TypedValue extends PointerType {} + class TypedValueList extends PointerType {} + class TypedValueListIter extends PointerType {} + class RelResult extends PointerType {} + class RelResultIter extends PointerType {} + class TxReport extends PointerType {} + class InProgress extends PointerType {} + class InProgressBuilder extends PointerType {} + class EntityBuilder extends PointerType {} + + Store store_open(String dbPath); void destroy(Pointer obj); - void query_builder_destroy(Pointer obj); - void store_destroy(Pointer obj); - void typed_value_destroy(Pointer obj); - void typed_value_list_destroy(Pointer obj); - void typed_value_list_iter_destroy(Pointer obj); - 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); + void uuid_destroy(Pointer obj); + void query_builder_destroy(QueryBuilder obj); + void store_destroy(Store obj); + void typed_value_destroy(TypedValue obj); + void typed_value_list_destroy(TypedValueList obj); + void typed_value_list_iter_destroy(TypedValueListIter obj); + void typed_value_result_set_destroy(RelResult obj); + void typed_value_result_set_iter_destroy(RelResultIter obj); + void tx_report_destroy(TxReport obj); + void in_progress_destroy(InProgress obj); + void in_progress_builder_destroy(InProgressBuilder obj); + void entity_builder_destroy(EntityBuilder obj); + void rust_c_string_destroy(Pointer str); // 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); + void store_cache_attribute_forward(Store store, String attribute, RustError.ByReference err); + void store_cache_attribute_reverse(Store store, String attribute, RustError.ByReference err); + void store_cache_attribute_bi_directional(Store store, String attribute, RustError.ByReference err); // 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); + TxReport store_transact(Store store, String transaction, RustError.ByReference err); + Pointer tx_report_entity_for_temp_id(TxReport report, String tempid); // returns a pointer to a 64 bit int on the heap + long tx_report_get_entid(TxReport report); + long tx_report_get_tx_instant(TxReport report); + InProgress store_begin_transaction(Store store, RustError.ByReference error); // 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); + TxReport in_progress_transact(InProgress in_progress, String transaction, RustError.ByReference err); + void in_progress_commit(InProgress in_progress, RustError.ByReference err); + void in_progress_rollback(InProgress in_progress, RustError.ByReference err); + InProgressBuilder in_progress_builder(InProgress in_progress); + EntityBuilder in_progress_entity_builder_from_temp_id(InProgress in_progress, String temp_id); + EntityBuilder in_progress_entity_builder_from_entid(InProgress 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); + InProgressBuilder store_in_progress_builder(Store store, RustError.ByReference err); + void in_progress_builder_add_string(InProgressBuilder builder, long entid, String kw, String value, RustError.ByReference err); + void in_progress_builder_add_long(InProgressBuilder builder, long entid, String kw, long value, RustError.ByReference err); + void in_progress_builder_add_ref(InProgressBuilder builder, long entid, String kw, long value, RustError.ByReference err); + void in_progress_builder_add_keyword(InProgressBuilder builder, long entid, String kw, String value, RustError.ByReference err); + void in_progress_builder_add_timestamp(InProgressBuilder builder, long entid, String kw, long value, RustError.ByReference err); + void in_progress_builder_add_boolean(InProgressBuilder builder, long entid, String kw, int value, RustError.ByReference err); + void in_progress_builder_add_double(InProgressBuilder builder, long entid, String kw, double value, RustError.ByReference err); + void in_progress_builder_add_uuid(InProgressBuilder builder, long entid, String kw, Pointer value, RustError.ByReference err); + void in_progress_builder_retract_string(InProgressBuilder builder, long entid, String kw, String value, RustError.ByReference err); + void in_progress_builder_retract_long(InProgressBuilder builder, long entid, String kw, long value, RustError.ByReference err); + void in_progress_builder_retract_ref(InProgressBuilder builder, long entid, String kw, long value, RustError.ByReference err); + void in_progress_builder_retract_keyword(InProgressBuilder builder, long entid, String kw, String value, RustError.ByReference err); + void in_progress_builder_retract_timestamp(InProgressBuilder builder, long entid, String kw, long value, RustError.ByReference err); + void in_progress_builder_retract_boolean(InProgressBuilder builder, long entid, String kw, int value, RustError.ByReference err); + void in_progress_builder_retract_double(InProgressBuilder builder, long entid, String kw, double value, RustError.ByReference err); + void in_progress_builder_retract_uuid(InProgressBuilder builder, long entid, String kw, Pointer value, RustError.ByReference err); + InProgressTransactionResult.ByValue in_progress_builder_transact(InProgressBuilder builder); + TxReport in_progress_builder_commit(InProgressBuilder builder, RustError.ByReference err); // 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); + EntityBuilder store_entity_builder_from_temp_id(Store store, String temp_id, RustError.ByReference err); + EntityBuilder store_entity_builder_from_entid(Store store, long entid, RustError.ByReference err); + void entity_builder_add_string(EntityBuilder builder, String kw, String value, RustError.ByReference err); + void entity_builder_add_long(EntityBuilder builder, String kw, long value, RustError.ByReference err); + void entity_builder_add_ref(EntityBuilder builder, String kw, long value, RustError.ByReference err); + void entity_builder_add_keyword(EntityBuilder builder, String kw, String value, RustError.ByReference err); + void entity_builder_add_boolean(EntityBuilder builder, String kw, int value, RustError.ByReference err); + void entity_builder_add_double(EntityBuilder builder, String kw, double value, RustError.ByReference err); + void entity_builder_add_timestamp(EntityBuilder builder, String kw, long value, RustError.ByReference err); + void entity_builder_add_uuid(EntityBuilder builder, String kw, Pointer value, RustError.ByReference err); + void entity_builder_retract_string(EntityBuilder builder, String kw, String value, RustError.ByReference err); + void entity_builder_retract_long(EntityBuilder builder, String kw, long value, RustError.ByReference err); + void entity_builder_retract_ref(EntityBuilder builder, String kw, long value, RustError.ByReference err); + void entity_builder_retract_keyword(EntityBuilder builder, String kw, String value, RustError.ByReference err); + void entity_builder_retract_boolean(EntityBuilder builder, String kw, int value, RustError.ByReference err); + void entity_builder_retract_double(EntityBuilder builder, String kw, double value, RustError.ByReference err); + void entity_builder_retract_timestamp(EntityBuilder builder, String kw, long value, RustError.ByReference err); + void entity_builder_retract_uuid(EntityBuilder builder, String kw, Pointer value, RustError.ByReference err); + InProgressTransactionResult.ByValue entity_builder_transact(EntityBuilder builder); + TxReport entity_builder_commit(EntityBuilder builder, RustError.ByReference err); // observers - void store_register_observer(Pointer store, String key, Pointer attributes, int len, TxObserverCallback callback); - void store_unregister_observer(Pointer store, String key); - long store_entid_for_attribute(Pointer store, String attr); + void store_register_observer(Store store, String key, Pointer attributes, int len, TxObserverCallback callback); + void store_unregister_observer(Store store, String key); + long store_entid_for_attribute(Store store, String attr); // Query Building - Pointer store_query(Pointer store, String query); - RustResult store_value_for_attribute(Pointer store, long entid, String attribute); - void query_builder_bind_long(Pointer query, String var, long value); - void query_builder_bind_ref(Pointer query, String var, long value); - void query_builder_bind_ref_kw(Pointer query, String var, String value); - void query_builder_bind_kw(Pointer query, String var, String value); - void query_builder_bind_boolean(Pointer query, String var, int value); - void query_builder_bind_double(Pointer query, String var, double value); - void query_builder_bind_timestamp(Pointer query, String var, long value); - void query_builder_bind_string(Pointer query, String var, String value); - void query_builder_bind_uuid(Pointer query, String var, Pointer value); + QueryBuilder store_query(Store store, String query); + TypedValue store_value_for_attribute(Store store, long entid, String attribute, RustError.ByReference err); + void query_builder_bind_long(QueryBuilder query, String var, long value); + void query_builder_bind_ref(QueryBuilder query, String var, long value); + void query_builder_bind_ref_kw(QueryBuilder query, String var, String value); + void query_builder_bind_kw(QueryBuilder query, String var, String value); + void query_builder_bind_boolean(QueryBuilder query, String var, int value); + void query_builder_bind_double(QueryBuilder query, String var, double value); + void query_builder_bind_timestamp(QueryBuilder query, String var, long value); + void query_builder_bind_string(QueryBuilder query, String var, String value); + void query_builder_bind_uuid(QueryBuilder query, String var, Pointer value); // Query Execution - RustResult query_builder_execute(Pointer query); - RustResult query_builder_execute_scalar(Pointer query); - RustResult query_builder_execute_coll(Pointer query); - RustResult query_builder_execute_tuple(Pointer query); + RelResult query_builder_execute(QueryBuilder query, RustError.ByReference err); + TypedValue query_builder_execute_scalar(QueryBuilder query, RustError.ByReference err); + TypedValueList query_builder_execute_coll(QueryBuilder query, RustError.ByReference err); + TypedValueList query_builder_execute_tuple(QueryBuilder query, RustError.ByReference err); // Query Result Processing - long typed_value_into_long(Pointer value); - long typed_value_into_entid(Pointer value); - String typed_value_into_kw(Pointer value); - String typed_value_into_string(Pointer value); - Pointer typed_value_into_uuid(Pointer value); - int typed_value_into_boolean(Pointer value); - double typed_value_into_double(Pointer value); - long typed_value_into_timestamp(Pointer value); - Pointer typed_value_value_type(Pointer value); + long typed_value_into_long(TypedValue value); + long typed_value_into_entid(TypedValue value); + Pointer typed_value_into_kw(TypedValue value); + Pointer typed_value_into_string(TypedValue value); + Pointer typed_value_into_uuid(TypedValue value); + int typed_value_into_boolean(TypedValue value); + double typed_value_into_double(TypedValue value); + long typed_value_into_timestamp(TypedValue value); + int typed_value_value_type(TypedValue value); - Pointer row_at_index(Pointer rows, int index); - Pointer typed_value_result_set_into_iter(Pointer rows); - Pointer typed_value_result_set_iter_next(Pointer iter); + TypedValueList row_at_index(RelResult rows, int index); + RelResultIter typed_value_result_set_into_iter(RelResult rows); + TypedValueList typed_value_result_set_iter_next(RelResultIter iter); - Pointer typed_value_list_into_iter(Pointer rows); - Pointer typed_value_list_iter_next(Pointer iter); + TypedValueListIter typed_value_list_into_iter(TypedValueList rows); + TypedValue typed_value_list_iter_next(TypedValueListIter iter); - Pointer value_at_index(Pointer rows, int index); - long value_at_index_into_long(Pointer rows, int index); - long value_at_index_into_entid(Pointer rows, int index); - 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); - 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); + TypedValue value_at_index(TypedValueList rows, int index); + long value_at_index_into_long(TypedValueList rows, int index); + long value_at_index_into_entid(TypedValueList rows, int index); + Pointer value_at_index_into_kw(TypedValueList rows, int index); + Pointer value_at_index_into_string(TypedValueList rows, int index); + Pointer value_at_index_into_uuid(TypedValueList rows, int index); + int value_at_index_into_boolean(TypedValueList rows, int index); + double value_at_index_into_double(TypedValueList rows, int index); + long value_at_index_into_timestamp(TypedValueList rows, int index); } diff --git a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/Mentat.java b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/Mentat.java index 84510440..94f4f285 100644 --- a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/Mentat.java +++ b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/Mentat.java @@ -20,7 +20,7 @@ import com.sun.jna.Pointer; * This class provides all of the basic API that can be found in Mentat's Store struct.
* The raw pointer it holds is a pointer to a Store. */ -public class Mentat extends RustObject { +public class Mentat extends RustObject { static { System.loadLibrary("mentat_ffi"); @@ -32,21 +32,21 @@ public class Mentat extends RustObject { * @param dbPath The URI as a String of the store to open. */ public Mentat(String dbPath) { - this.rawPointer = JNA.INSTANCE.store_open(dbPath); + this(JNA.INSTANCE.store_open(dbPath)); } /** * Open a connection to an in-memory Store. */ public Mentat() { - this.rawPointer = JNA.INSTANCE.store_open(""); + this(JNA.INSTANCE.store_open("")); } /** * Create a new Mentat with the provided pointer to a Mentat Store * @param rawPointer A pointer to a Mentat Store. */ - public Mentat(Pointer rawPointer) { this.rawPointer = rawPointer; } + public Mentat(JNA.Store rawPointer) { super(rawPointer); } /** * Add an attribute to the cache. The {@link CacheDirection} determines how that attribute can be @@ -63,18 +63,19 @@ public class Mentat extends RustObject { * BOTH adds an attribute such that it is cached in both directions. */ public void cache(String attribute, CacheDirection direction) { - RustResult result = null; + RustError.ByReference err = new RustError.ByReference(); switch (direction) { case FORWARD: - result = JNA.INSTANCE.store_cache_attribute_forward(this.rawPointer, attribute); + JNA.INSTANCE.store_cache_attribute_forward(this.validPointer(), attribute, err); + break; case REVERSE: - result = JNA.INSTANCE.store_cache_attribute_reverse(this.rawPointer, attribute); + JNA.INSTANCE.store_cache_attribute_reverse(this.validPointer(), attribute, err); + break; case BOTH: - result = JNA.INSTANCE.store_cache_attribute_bi_directional(this.rawPointer, attribute); - } - if (result.isFailure()) { - Log.e("Mentat", result.err); + JNA.INSTANCE.store_cache_attribute_bi_directional(this.validPointer(), attribute, err); + break; } + err.logAndConsumeError("Mentat"); } /** @@ -84,15 +85,12 @@ public class Mentat extends RustObject { * @return The {@link TxReport} of the completed transaction */ public TxReport transact(String transaction) { - RustResult result = JNA.INSTANCE.store_transact(this.rawPointer, transaction); - if (result.isFailure()) { - Log.e("Mentat", result.err); - return null; - } - - if (result.isSuccess()) { - return new TxReport(result.ok); + RustError.ByReference err = new RustError.ByReference(); + JNA.TxReport report = JNA.INSTANCE.store_transact(this.validPointer(), transaction, err); + if (err.isSuccess()) { + return new TxReport(report); } else { + err.logAndConsumeError("Mentat"); return null; } } @@ -103,7 +101,7 @@ public class Mentat extends RustObject { * @return The `Entid` associated with the attribute. */ public long entIdForAttribute(String attribute) { - return JNA.INSTANCE.store_entid_for_attribute(this.rawPointer, attribute); + return JNA.INSTANCE.store_entid_for_attribute(this.validPointer(), attribute); } /** @@ -112,7 +110,7 @@ public class Mentat extends RustObject { * @return The {@link Query} representing the query that can be executed. */ public Query query(String query) { - return new Query(JNA.INSTANCE.store_query(this.rawPointer, query)); + return new Query(JNA.INSTANCE.store_query(this.validPointer(), query)); } /** @@ -123,16 +121,14 @@ public class Mentat extends RustObject { * @return The {@link TypedValue} containing the value of the attribute for the entity. */ public TypedValue valueForAttributeOfEntity(String attribute, long entid) { - RustResult result = JNA.INSTANCE.store_value_for_attribute(this.rawPointer, entid, attribute); + RustError.ByReference err = new RustError.ByReference(); + JNA.TypedValue typedVal = JNA.INSTANCE.store_value_for_attribute(this.validPointer(), entid, attribute, err); - if (result.isSuccess()) { - return new TypedValue(result.ok); - } - - if (result.isFailure()) { - Log.e("Mentat", result.err); + if (err.isSuccess()) { + return new TypedValue(typedVal); } + err.logAndConsumeError("Mentat"); return null; } @@ -149,11 +145,11 @@ public class Mentat extends RustObject { // turn string array into int array long[] attrEntids = new long[attributes.length]; for(int i = 0; i < attributes.length; i++) { - attrEntids[i] = JNA.INSTANCE.store_entid_for_attribute(this.rawPointer, attributes[i]); + attrEntids[i] = JNA.INSTANCE.store_entid_for_attribute(this.validPointer(), attributes[i]); } final Pointer entidsNativeArray = new Memory(8 * attrEntids.length); entidsNativeArray.write(0, attrEntids, 0, attrEntids.length); - JNA.INSTANCE.store_register_observer(rawPointer, key, entidsNativeArray, attrEntids.length, callback); + JNA.INSTANCE.store_register_observer(validPointer(), key, entidsNativeArray, attrEntids.length, callback); } /** @@ -164,10 +160,9 @@ public class Mentat extends RustObject { * @param key String representing an identifier for the observer. */ public void unregisterObserver(String key) { - JNA.INSTANCE.store_unregister_observer(rawPointer, key); + JNA.INSTANCE.store_unregister_observer(validPointer(), key); } - /** * Start a new transaction * @@ -176,15 +171,12 @@ public class Mentat extends RustObject { * @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); + RustError.ByReference err = new RustError.ByReference(); + JNA.InProgress inProg = JNA.INSTANCE.store_begin_transaction(this.validPointer(), err); + if (err.isSuccess()) { + return new InProgress(inProg); } - - if (result.isFailure()) { - Log.i("Mentat", result.err); - } - + err.logAndConsumeError("Mentat"); return null; } @@ -197,15 +189,12 @@ public class Mentat extends RustObject { * @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); + RustError.ByReference err = new RustError.ByReference(); + JNA.InProgressBuilder builder = JNA.INSTANCE.store_in_progress_builder(this.validPointer(), err); + if (err.isSuccess()) { + return new InProgressBuilder(builder); } - - if (result.isFailure()) { - Log.i("Mentat", result.err); - } - + err.logAndConsumeError("Mentat"); return null; } @@ -219,15 +208,12 @@ public class Mentat extends RustObject { * @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); + RustError.ByReference err = new RustError.ByReference(); + JNA.EntityBuilder builder = JNA.INSTANCE.store_entity_builder_from_entid(this.validPointer(), entid, err); + if (err.isSuccess()) { + return new EntityBuilder(builder); } - - if (result.isFailure()) { - Log.i("Mentat", result.err); - } - + err.logAndConsumeError("Mentat"); return null; } @@ -241,22 +227,17 @@ public class Mentat extends RustObject { * @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); + RustError.ByReference err = new RustError.ByReference(); + JNA.EntityBuilder builder = JNA.INSTANCE.store_entity_builder_from_temp_id(this.validPointer(), tempId, err); + if (err.isSuccess()) { + return new EntityBuilder(builder); } - - if (result.isFailure()) { - Log.i("Mentat", result.err); - } - + err.logAndConsumeError("Mentat"); return null; } @Override - public void close() { - if (this.rawPointer != null) { - JNA.INSTANCE.store_destroy(this.rawPointer); - } + protected void destroyPointer(JNA.Store p) { + JNA.INSTANCE.store_destroy(p); } } diff --git a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/Query.java b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/Query.java index 37eacb0a..aa389d2d 100644 --- a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/Query.java +++ b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/Query.java @@ -12,8 +12,6 @@ package org.mozilla.mentat; import android.util.Log; -import com.sun.jna.Pointer; - import java.util.Date; import java.util.UUID; @@ -106,10 +104,10 @@ import java.util.UUID; * }); * */ -public class Query extends RustObject { +public class Query extends RustObject { - public Query(Pointer pointer) { - this.rawPointer = pointer; + public Query(JNA.QueryBuilder pointer) { + super(pointer); } /** @@ -119,9 +117,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 bind(String varName, long value) { - this.validate(); - JNA.INSTANCE.query_builder_bind_long(this.rawPointer, varName, value); + public Query bind(String varName, long value) { + this.assertValidPointer(); + JNA.INSTANCE.query_builder_bind_long(this.validPointer(), varName, value); return this; } @@ -132,9 +130,8 @@ public class Query extends RustObject { * @param value The value to be bound * @return This {@link Query} such that further function can be called. */ - Query bindEntidReference(String varName, long value) { - this.validate(); - JNA.INSTANCE.query_builder_bind_ref(this.rawPointer, varName, value); + public Query bindEntidReference(String varName, long value) { + JNA.INSTANCE.query_builder_bind_ref(this.validPointer(), varName, value); return this; } @@ -145,9 +142,8 @@ public class Query extends RustObject { * @param value The value to be bound * @return This {@link Query} such that further function can be called. */ - Query bindKeywordReference(String varName, String value) { - this.validate(); - JNA.INSTANCE.query_builder_bind_ref_kw(this.rawPointer, varName, value); + public Query bindKeywordReference(String varName, String value) { + JNA.INSTANCE.query_builder_bind_ref_kw(this.validPointer(), varName, value); return this; } @@ -158,9 +154,8 @@ public class Query extends RustObject { * @param value The value to be bound * @return This {@link Query} such that further function can be called. */ - Query bindKeyword(String varName, String value) { - this.validate(); - JNA.INSTANCE.query_builder_bind_kw(this.rawPointer, varName, value); + public Query bindKeyword(String varName, String value) { + JNA.INSTANCE.query_builder_bind_kw(this.validPointer(), varName, value); return this; } @@ -171,9 +166,8 @@ public class Query extends RustObject { * @param value The value to be bound * @return This {@link Query} such that further function can be called. */ - Query bind(String varName, boolean value) { - this.validate(); - JNA.INSTANCE.query_builder_bind_boolean(this.rawPointer, varName, value ? 1 : 0); + public Query bind(String varName, boolean value) { + JNA.INSTANCE.query_builder_bind_boolean(this.validPointer(), varName, value ? 1 : 0); return this; } @@ -184,9 +178,8 @@ public class Query extends RustObject { * @param value The value to be bound * @return This {@link Query} such that further function can be called. */ - Query bind(String varName, double value) { - this.validate(); - JNA.INSTANCE.query_builder_bind_double(this.rawPointer, varName, value); + public Query bind(String varName, double value) { + JNA.INSTANCE.query_builder_bind_double(this.validPointer(), varName, value); return this; } @@ -197,10 +190,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 bind(String varName, Date value) { - this.validate(); + public Query bind(String varName, Date value) { long timestamp = value.getTime() * 1000; - JNA.INSTANCE.query_builder_bind_timestamp(this.rawPointer, varName, timestamp); + JNA.INSTANCE.query_builder_bind_timestamp(this.validPointer(), varName, timestamp); return this; } @@ -211,9 +203,8 @@ public class Query extends RustObject { * @param value The value to be bound * @return This {@link Query} such that further function can be called. */ - Query bind(String varName, String value) { - this.validate(); - JNA.INSTANCE.query_builder_bind_string(this.rawPointer, varName, value); + public Query bind(String varName, String value) { + JNA.INSTANCE.query_builder_bind_string(this.validPointer(), varName, value); return this; } @@ -224,9 +215,8 @@ public class Query extends RustObject { * @param value The value to be bound * @return This {@link Query} such that further function can be called. */ - Query bind(String varName, UUID value) { - this.validate(); - JNA.INSTANCE.query_builder_bind_uuid(this.rawPointer, varName, getPointerForUUID(value)); + public Query bind(String varName, UUID value) { + JNA.INSTANCE.query_builder_bind_uuid(this.validPointer(), varName, getPointerForUUID(value)); return this; } @@ -236,16 +226,14 @@ 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 run(final RelResultHandler handler) { - this.validate(); - RustResult result = JNA.INSTANCE.query_builder_execute(rawPointer); - rawPointer = null; - - if (result.isFailure()) { - Log.e("Query", result.err); + public void run(final RelResultHandler handler) { + RustError.ByReference error = new RustError.ByReference(); + JNA.RelResult relResult = JNA.INSTANCE.query_builder_execute(this.consumePointer(), error); + if (error.isFailure()) { + Log.e("Query", error.consumeErrorMessage()); return; } - handler.handleRows(new RelResult(result.ok)); + handler.handleRows(new RelResult(relResult)); } /** @@ -254,18 +242,17 @@ 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 run(final ScalarResultHandler handler) { - this.validate(); - RustResult result = JNA.INSTANCE.query_builder_execute_scalar(rawPointer); - rawPointer = null; + public void run(final ScalarResultHandler handler) { + RustError.ByReference error = new RustError.ByReference(); + JNA.TypedValue valOrNull = JNA.INSTANCE.query_builder_execute_scalar(consumePointer(), error); - if (result.isFailure()) { - Log.e("Query", result.err); + if (error.isFailure()) { + Log.e("Query", error.consumeErrorMessage()); return; } - if (result.isSuccess()) { - handler.handleValue(new TypedValue(result.ok)); + if (valOrNull != null) { + handler.handleValue(new TypedValue(valOrNull)); } else { handler.handleValue(null); } @@ -277,16 +264,15 @@ 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 run(final CollResultHandler handler) { - this.validate(); - RustResult result = JNA.INSTANCE.query_builder_execute_coll(rawPointer); - rawPointer = null; + public void run(final CollResultHandler handler) { + RustError.ByReference error = new RustError.ByReference(); + JNA.TypedValueList collResult = JNA.INSTANCE.query_builder_execute_coll(this.consumePointer(), error); - if (result.isFailure()) { - Log.e("Query", result.err); + if (error.isFailure()) { + Log.e("Query", error.consumeErrorMessage()); return; } - handler.handleList(new CollResult(result.ok)); + handler.handleList(new CollResult(collResult)); } /** @@ -295,28 +281,24 @@ 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 run(final TupleResultHandler handler) { - this.validate(); - RustResult result = JNA.INSTANCE.query_builder_execute_tuple(rawPointer); - rawPointer = null; + public void run(final TupleResultHandler handler) { + RustError.ByReference error = new RustError.ByReference(); + JNA.TypedValueList tuple = JNA.INSTANCE.query_builder_execute_tuple(this.consumePointer(), error); - if (result.isFailure()) { - Log.e("Query", result.err); + if (error.isFailure()) { + Log.e("Query", error.consumeErrorMessage()); return; } - if (result.isSuccess()) { - handler.handleRow(new TupleResult(result.ok)); + if (tuple != null) { + handler.handleRow(new TupleResult(tuple)); } else { handler.handleRow(null); } } @Override - public void close() { - if (this.rawPointer == null) { - return; - } - JNA.INSTANCE.query_builder_destroy(this.rawPointer); + protected void destroyPointer(JNA.QueryBuilder p) { + JNA.INSTANCE.query_builder_destroy(p); } } diff --git a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/RelResult.java b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/RelResult.java index 6e1eb4c1..61cae67f 100644 --- a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/RelResult.java +++ b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/RelResult.java @@ -10,8 +10,6 @@ package org.mozilla.mentat; -import com.sun.jna.Pointer; - /** * Wraps a `Rel` result from a Mentat query. * A `Rel` result is a list of rows of `TypedValues`. @@ -44,10 +42,10 @@ import com.sun.jna.Pointer; *

* Note that iteration is consuming and can only be done once. */ -public class RelResult extends RustObject implements Iterable { +public class RelResult extends RustObject implements Iterable { - public RelResult(Pointer pointer) { - this.rawPointer = pointer; + public RelResult(JNA.RelResult pointer) { + super(pointer); } /** @@ -57,8 +55,8 @@ public class RelResult extends RustObject implements Iterable { * @return The row at the requested index as a `TupleResult`, if present, or nil if there is no row at that index. */ public TupleResult rowAtIndex(int index) { - this.validate(); - Pointer pointer = JNA.INSTANCE.row_at_index(this.rawPointer, index); + this.assertValidPointer(); + JNA.TypedValueList pointer = JNA.INSTANCE.row_at_index(this.validPointer(), index); if (pointer == null) { return null; } @@ -68,19 +66,12 @@ public class RelResult extends RustObject implements Iterable { @Override public RelResultIterator iterator() { - this.validate(); - Pointer iterPointer = JNA.INSTANCE.typed_value_result_set_into_iter(this.rawPointer); - this.rawPointer = null; - if (iterPointer == null) { - return null; - } + JNA.RelResultIter iterPointer = JNA.INSTANCE.typed_value_result_set_into_iter(this.consumePointer()); return new RelResultIterator(iterPointer); } @Override - public void close() { - if (this.rawPointer != null) { - JNA.INSTANCE.typed_value_result_set_destroy(this.rawPointer); - } + protected void destroyPointer(JNA.RelResult p) { + JNA.INSTANCE.typed_value_result_set_destroy(p); } } diff --git a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/RelResultIterator.java b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/RelResultIterator.java index 2ba37694..fea64b5a 100644 --- a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/RelResultIterator.java +++ b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/RelResultIterator.java @@ -12,43 +12,27 @@ package org.mozilla.mentat; import com.sun.jna.Pointer; -import java.util.Iterator; /** * Iterator for a {@link RelResult} */ -public class RelResultIterator extends RustObject implements Iterator { +public class RelResultIterator extends RustIterator { - Pointer nextPointer; - - RelResultIterator(Pointer iterator) { - this.rawPointer = iterator; - } - - private Pointer getNextPointer() { - return JNA.INSTANCE.typed_value_result_set_iter_next(this.rawPointer); + RelResultIterator(JNA.RelResultIter iterator) { + super(iterator); } @Override - public boolean hasNext() { - this.nextPointer = getNextPointer(); - return this.nextPointer != null; + protected JNA.TypedValueList advanceIterator() { + return JNA.INSTANCE.typed_value_result_set_iter_next(this.validPointer()); } @Override - public TupleResult next() { - Pointer next = this.nextPointer == null ? getNextPointer() : this.nextPointer; - if (next == null) { - return null; - } - - return new TupleResult(next); + protected TupleResult constructItem(JNA.TypedValueList p) { + return new TupleResult(p); } - @Override - public void close() { - if (this.rawPointer != null) { - JNA.INSTANCE.typed_value_result_set_iter_destroy(this.rawPointer); - } + protected void destroyPointer(JNA.RelResultIter p) { + JNA.INSTANCE.typed_value_result_set_iter_destroy(p); } } diff --git a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/RustError.java b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/RustError.java new file mode 100644 index 00000000..c403f8fe --- /dev/null +++ b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/RustError.java @@ -0,0 +1,86 @@ +/* -*- 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 org.mozilla.mentat; + +import android.util.Log; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; + +import java.util.Arrays; +import java.util.List; + +/** + * Represents a C struct containing a {@link Pointer}s and String that map to a Rust Result. + * A RustResult will contain either an ok value, OR an err value, or neither - never both. + */ +public class RustError extends Structure { + public static class ByReference extends RustError implements Structure.ByReference { + } + + public static class ByValue extends RustError implements Structure.ByValue { + } + // It's probably a mistake to touch this, but it needs to be public for JNA + public Pointer message; + + /** + * Does this represent success? + */ + public boolean isSuccess() { + return this.message == null; + } + + /** + * Does this represent failure? + */ + public boolean isFailure() { + return this.message != null; + } + + /** + * Get and consume the error message, or null if there is none. + */ + public String consumeErrorMessage() { + String result = this.getErrorMessage(); + if (this.message != null) { + JNA.INSTANCE.rust_c_string_destroy(this.message); + this.message = null; + } + return result; + } + + /** + * Get the error message or null if there is none. + */ + public String getErrorMessage() { + return this.message == null ? null : this.message.getString(0, "utf8"); + } + + @Override + protected List getFieldOrder() { + return Arrays.asList("message"); + } + + @Override + protected void finalize() { + if (this.message != null) { + JNA.INSTANCE.rust_c_string_destroy(this.message); + this.message = null; + } + } + + /* package-local */ + void logAndConsumeError(String tag) { + if (this.isFailure()) { + Log.e(tag, this.consumeErrorMessage()); + } + } +} \ No newline at end of file diff --git a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/RustIterator.java b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/RustIterator.java new file mode 100644 index 00000000..171b7655 --- /dev/null +++ b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/RustIterator.java @@ -0,0 +1,70 @@ +/* -*- 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 org.mozilla.mentat; + +import com.sun.jna.Pointer; +import com.sun.jna.PointerType; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +// Common factored-out code shared by both RelResultIterator and CollResultIterator (and possibly +// others in the future). This code is a bit error-prone so it's worth avoiding the duplication. +abstract class RustIterator> extends RustObject implements Iterator { + // We own this if it is not null! + private ItemPtr nextPointer; + + RustIterator(T pointer) { + super(pointer); + } + + /** Implement by calling `JNA.INSTANCE.whatever_iter_next(this.validPointer());` */ + abstract protected ItemPtr advanceIterator(); + // Generally should be implemented as `new E(p)`. + abstract protected E constructItem(ItemPtr p); + + private ItemPtr consumeNextPointer() { + if (this.nextPointer == null) { + throw new NullPointerException(this.getClass() + " nextPointer already consumed"); + } + ItemPtr p = this.nextPointer; + this.nextPointer = null; + return p; + } + + @Override + public boolean hasNext() { + if (this.nextPointer != null) { + return true; + } + this.nextPointer = advanceIterator(); + return this.nextPointer != null; + } + + @Override + public E next() { + ItemPtr next = this.nextPointer == null ? advanceIterator() : this.consumeNextPointer(); + if (next == null) { + throw new NoSuchElementException(); + } + return this.constructItem(next); + } + + @Override + public void close() { + if (this.nextPointer != null) { + // Clean up the next pointer by delegating to the iterated item -- this simplifies + // iterator implementations, and keeps the cleanup code in one place. + this.constructItem(this.consumeNextPointer()); + } + super.close(); + } +} diff --git a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/RustObject.java b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/RustObject.java index bbc45bc0..27395c72 100644 --- a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/RustObject.java +++ b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/RustObject.java @@ -10,32 +10,58 @@ package org.mozilla.mentat; +import android.util.Log; + import com.sun.jna.Memory; import com.sun.jna.Pointer; +import com.sun.jna.PointerType; -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. - * This class implements {@link Closeable} but does not provide an implementation, forcing all + * This class implements {@link AutoCloseable} but does not provide an implementation, forcing all * subclasses to implement it. This ensures that all classes that inherit from RustObject * will have their {@link Pointer} destroyed when the Java wrapper is destroyed. */ -abstract class RustObject implements Closeable { - Pointer rawPointer; +abstract class RustObject implements AutoCloseable { + // This should probably be private to let us better prevent usage mistakes (which lead to + // memory-unsafety). + private T rawPointer; + + RustObject(T p) { + rawPointer = p; + } /** * Throws a {@link NullPointerException} if the underlying {@link Pointer} is null. */ - void validate() { - if (this.rawPointer == null) { + void assertValidPointer() { + if (this.isConsumed()) { throw new NullPointerException(this.getClass() + " consumed"); } } - public Pointer getPointerForUUID(UUID uuid) { + T validPointer() { + this.assertValidPointer(); + return this.rawPointer; + } + + boolean isConsumed() { + return this.rawPointer == null; + } + + /* package-local */ + T consumePointer() { + this.assertValidPointer(); + T p = this.rawPointer; + this.rawPointer = null; + return p; + } + + /* package-local */ + static Pointer getPointerForUUID(UUID uuid) { ByteBuffer bb = ByteBuffer.wrap(new byte[16]); bb.putLong(uuid.getMostSignificantBits()); bb.putLong(uuid.getLeastSignificantBits()); @@ -45,11 +71,46 @@ abstract class RustObject implements Closeable { 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); + /* package-local */ + static UUID getAndConsumeUUIDPointer(Pointer uuidPtr) { + try { + byte[] bytes = uuidPtr.getByteArray(0, 16); + ByteBuffer bb = ByteBuffer.wrap(bytes); + long high = bb.getLong(); + long low = bb.getLong(); + return new UUID(high, low); + } finally { + JNA.INSTANCE.uuid_destroy(uuidPtr); + } + } + + /* package-local */ + static String getAndConsumeMentatString(Pointer stringPtr) { + if (stringPtr == null) { + return null; + } + try { + return stringPtr.getString(0, "utf8"); + } finally { + JNA.INSTANCE.rust_c_string_destroy(stringPtr); + } + } + + abstract protected void destroyPointer(T p); + + @Override + public void close() { + if (this.rawPointer != null) { + this.destroyPointer(this.consumePointer()); + } + } + + @Override + protected void finalize() { + try { + this.close(); + } catch (Exception e) { + Log.e("RustObject", e.toString()); + } } } diff --git a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/RustResult.java b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/RustResult.java deleted file mode 100644 index f2836bad..00000000 --- a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/RustResult.java +++ /dev/null @@ -1,62 +0,0 @@ -/* -*- 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 org.mozilla.mentat; - -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; - -/** - * Represents a C struct containing a {@link Pointer}s and String that map to a Rust Result. - * A RustResult will contain either an ok value, OR an err value, or neither - never both. - */ -public class RustResult extends Structure implements Closeable { - public static class ByReference extends RustResult implements Structure.ByReference { - } - - public static class ByValue extends RustResult implements Structure.ByValue { - } - - public Pointer ok; - public String err; - - /** - * Is there an value attached to this result - * @return true if a value is present, false otherwise - */ - public boolean isSuccess() { - return this.ok != null; - } - - /** - * Is there an error attached to this result? - * @return true is an error is present, false otherwise - */ - public boolean isFailure() { - return this.err != null; - } - - @Override - protected List getFieldOrder() { - return Arrays.asList("ok", "err"); - } - - @Override - public void close() throws IOException { - if (this.getPointer() != null) { - JNA.INSTANCE.destroy(this.getPointer()); - } - } -} \ No newline at end of file diff --git a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/TupleResult.java b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/TupleResult.java index 316d7fd8..ef5a6ed2 100644 --- a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/TupleResult.java +++ b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/TupleResult.java @@ -35,10 +35,10 @@ import java.util.UUID; *

* To iterate over the result set use standard iteration flows. */ -public class TupleResult extends RustObject { +public class TupleResult extends RustObject { - public TupleResult(Pointer pointer) { - this.rawPointer = pointer; + public TupleResult(JNA.TypedValueList pointer) { + super(pointer); } /** @@ -48,8 +48,7 @@ public class TupleResult extends RustObject { * @return The {@link TypedValue} at that index. */ public TypedValue get(Integer index) { - this.validate(); - Pointer pointer = JNA.INSTANCE.value_at_index(this.rawPointer, index); + JNA.TypedValue pointer = JNA.INSTANCE.value_at_index(this.validPointer(), index); if (pointer == null) { return null; } @@ -64,8 +63,7 @@ public class TupleResult extends RustObject { * @return The {@link Long} at that index. */ public Long asLong(Integer index) { - this.validate(); - return JNA.INSTANCE.value_at_index_into_long(this.rawPointer, index); + return JNA.INSTANCE.value_at_index_into_long(this.validPointer(), index); } /** @@ -76,8 +74,7 @@ public class TupleResult extends RustObject { * @return The Entid at that index. */ public Long asEntid(Integer index) { - this.validate(); - return JNA.INSTANCE.value_at_index_into_entid(this.rawPointer, index); + return JNA.INSTANCE.value_at_index_into_entid(this.validPointer(), index); } /** @@ -88,8 +85,7 @@ public class TupleResult extends RustObject { * @return The keyword at that index. */ public String asKeyword(Integer index) { - this.validate(); - return JNA.INSTANCE.value_at_index_into_kw(this.rawPointer, index); + return getAndConsumeMentatString(JNA.INSTANCE.value_at_index_into_kw(this.validPointer(), index)); } /** @@ -100,8 +96,7 @@ public class TupleResult extends RustObject { * @return The {@link Boolean} at that index. */ public Boolean asBool(Integer index) { - this.validate(); - return JNA.INSTANCE.value_at_index_into_boolean(this.rawPointer, index) == 0 ? false : true; + return JNA.INSTANCE.value_at_index_into_boolean(this.validPointer(), index) == 0 ? false : true; } /** @@ -112,8 +107,7 @@ public class TupleResult extends RustObject { * @return The {@link Double} at that index. */ public Double asDouble(Integer index) { - this.validate(); - return JNA.INSTANCE.value_at_index_into_double(this.rawPointer, index); + return JNA.INSTANCE.value_at_index_into_double(this.validPointer(), index); } /** @@ -124,8 +118,7 @@ public class TupleResult extends RustObject { * @return The {@link Date} at that index. */ public Date asDate(Integer index) { - this.validate(); - return new Date(JNA.INSTANCE.value_at_index_into_timestamp(this.rawPointer, index) * 1_000); + return new Date(JNA.INSTANCE.value_at_index_into_timestamp(this.validPointer(), index) * 1_000); } /** @@ -136,8 +129,8 @@ public class TupleResult extends RustObject { * @return The {@link String} at that index. */ public String asString(Integer index) { - this.validate(); - return JNA.INSTANCE.value_at_index_into_string(this.rawPointer, index); + return getAndConsumeMentatString( + JNA.INSTANCE.value_at_index_into_string(this.validPointer(), index)); } /** @@ -148,14 +141,12 @@ public class TupleResult extends RustObject { * @return The {@link UUID} at that index. */ public UUID asUUID(Integer index) { - this.validate(); - return getUUIDFromPointer(JNA.INSTANCE.value_at_index_into_uuid(this.rawPointer, index)); + return getAndConsumeUUIDPointer( + JNA.INSTANCE.value_at_index_into_uuid(this.validPointer(), index)); } @Override - public void close() { - if (this.rawPointer != null) { - JNA.INSTANCE.typed_value_list_destroy(this.rawPointer); - } + protected void destroyPointer(JNA.TypedValueList p) { + JNA.INSTANCE.typed_value_list_destroy(p); } } diff --git a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/TxChange.java b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/TxChange.java index 6b172bc0..691bd5ee 100644 --- a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/TxChange.java +++ b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/TxChange.java @@ -13,7 +13,6 @@ package org.mozilla.mentat; import com.sun.jna.Pointer; import com.sun.jna.Structure; -import java.io.Closeable; import java.util.Arrays; import java.util.List; @@ -22,27 +21,24 @@ import java.util.List; * These changes contain the transaction identifier, a {@link Pointer} to a list of affected attribute * Entids and the number of items that the list contains. */ -public class TxChange extends Structure implements Closeable { +public class TxChange extends Structure { public static class ByReference extends TxChange implements Structure.ByReference { } public static class ByValue extends TxChange implements Structure.ByValue { } - public int txid; + public long txid; public Pointer changes; - public int numberOfItems; - // Used by the Swift counterpart, JNA does this for us automagically. - // But we still need it here so that the number of fields and their order is correct - public int changes_len; + public long changes_len; /** * Get the affected attributes for this transaction * @return The changes as a list of Entids of affected attributes */ public List getChanges() { - final long[] array = (long[]) changes.getLongArray(0, numberOfItems); - Long[] longArray = new Long[numberOfItems]; + final long[] array = (long[]) changes.getLongArray(0, (int)changes_len); + Long[] longArray = new Long[(int)changes_len]; int idx = 0; for (long change: array) { longArray[idx++] = change; @@ -52,13 +48,8 @@ public class TxChange extends Structure implements Closeable { @Override protected List getFieldOrder() { - return Arrays.asList("txid", "changes", "changes_len", "numberOfItems"); + return Arrays.asList("txid", "changes", "changes_len"); } - @Override - public void close() { - if (this.getPointer() != null) { - JNA.INSTANCE.destroy(this.getPointer()); - } - } + // Note: Rust has ownership of this data. } diff --git a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/TxChangeList.java b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/TxChangeList.java index c90d3982..daa42fd1 100644 --- a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/TxChangeList.java +++ b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/TxChangeList.java @@ -12,14 +12,13 @@ package org.mozilla.mentat; import com.sun.jna.Structure; -import java.io.Closeable; import java.util.Arrays; import java.util.List; /** * Represents a C struct containing a list of {@link TxChange}s that occured. */ -public class TxChangeList extends Structure implements Closeable { +public class TxChangeList extends Structure { public static class ByReference extends TxChangeList implements Structure.ByReference { } @@ -27,30 +26,21 @@ public class TxChangeList extends Structure implements Closeable { } public TxChange.ByReference reports; - public int numberOfItems; - // Used by the Swift counterpart, JNA does this for us automagically. - // // But we still need it here so that the number of fields and their order is correct - public int len; + public long len; /** * Get the changes that occured * @return a list of {@link TxChange}s for the notification */ public List getReports() { - final TxChange[] array = (TxChange[]) reports.toArray(numberOfItems); + final TxChange[] array = (TxChange[]) reports.toArray((int)len); return Arrays.asList(array); } @Override protected List getFieldOrder() { - return Arrays.asList("reports", "numberOfItems", "len"); + return Arrays.asList("reports", "len"); } - @Override - public void close() { - final TxChange[] nativeReports = (TxChange[]) reports.toArray(numberOfItems); - for (TxChange nativeReport : nativeReports) { - nativeReport.close(); - } - } + // Note: Rust has ownership of this data. } diff --git a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/TxReport.java b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/TxReport.java index 2c82bb62..6782c9e5 100644 --- a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/TxReport.java +++ b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/TxReport.java @@ -33,14 +33,14 @@ import java.util.Date; * long aEntid = report.getEntidForTempId("a"); *} */ -public class TxReport extends RustObject { +public class TxReport extends RustObject { private Long txId; private Date txInstant; - public TxReport(Pointer pointer) { - this.rawPointer = pointer; + public TxReport(JNA.TxReport pointer) { + super(pointer); } /** @@ -49,7 +49,7 @@ public class TxReport extends RustObject { */ public Long getTxId() { if (this.txId == null) { - this.txId = JNA.INSTANCE.tx_report_get_entid(this.rawPointer); + this.txId = JNA.INSTANCE.tx_report_get_entid(this.validPointer()); } return this.txId; @@ -61,7 +61,7 @@ public class TxReport extends RustObject { */ public Date getTxInstant() { if (this.txInstant == null) { - this.txInstant = new Date(JNA.INSTANCE.tx_report_get_tx_instant(this.rawPointer)); + this.txInstant = new Date(JNA.INSTANCE.tx_report_get_tx_instant(this.validPointer())); } return this.txInstant; } @@ -72,18 +72,19 @@ public class TxReport extends RustObject { * @return The `Entid` for the temporary identifier, if present, otherwise `null`. */ public Long getEntidForTempId(String tempId) { - Pointer longPointer = JNA.INSTANCE.tx_report_entity_for_temp_id(this.rawPointer, tempId); + Pointer longPointer = JNA.INSTANCE.tx_report_entity_for_temp_id(this.validPointer(), tempId); if (longPointer == null) { return null; } - - return longPointer.getLong(0); + try { + return longPointer.getLong(0); + } finally { + JNA.INSTANCE.destroy(longPointer); + } } @Override - public void close() { - if (this.rawPointer != null) { - JNA.INSTANCE.tx_report_destroy(this.rawPointer); - } + protected void destroyPointer(JNA.TxReport p) { + JNA.INSTANCE.tx_report_destroy(p); } } diff --git a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/TypedValue.java b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/TypedValue.java index b73980bd..cb19ca8f 100644 --- a/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/TypedValue.java +++ b/sdks/android/Mentat/library/src/main/java/org/mozilla/mentat/TypedValue.java @@ -25,16 +25,12 @@ import java.util.UUID; * Also, due to the consuming nature of the FFI layer, this class also manages it's raw pointer, nilling it after calling the * FFI conversion function so that the underlying base class can manage cleanup. */ -public class TypedValue extends RustObject { +public class TypedValue extends RustObject { private Object value; - private boolean isConsumed() { - return this.rawPointer == null; - } - - public TypedValue(Pointer pointer) { - this.rawPointer = pointer; + public TypedValue(JNA.TypedValue pointer) { + super(pointer); } /** @@ -44,8 +40,7 @@ public class TypedValue extends RustObject { */ public Long asLong() { if (!this.isConsumed()) { - this.value = JNA.INSTANCE.typed_value_into_long(this.rawPointer); - this.rawPointer = null; + this.value = JNA.INSTANCE.typed_value_into_long(this.consumePointer()); } return (Long)value; } @@ -57,8 +52,7 @@ public class TypedValue extends RustObject { */ public Long asEntid() { if (!this.isConsumed()) { - this.value = JNA.INSTANCE.typed_value_into_entid(this.rawPointer); - this.rawPointer = null; + this.value = JNA.INSTANCE.typed_value_into_entid(this.consumePointer()); } return (Long)value; } @@ -70,8 +64,7 @@ public class TypedValue extends RustObject { */ public String asKeyword() { if (!this.isConsumed()) { - this.value = JNA.INSTANCE.typed_value_into_kw(this.rawPointer); - this.rawPointer = null; + this.value = getAndConsumeMentatString(JNA.INSTANCE.typed_value_into_kw(this.consumePointer())); } return (String)value; } @@ -83,9 +76,8 @@ public class TypedValue extends RustObject { */ public Boolean asBoolean() { if (!this.isConsumed()) { - long value = JNA.INSTANCE.typed_value_into_boolean(this.rawPointer); + long value = JNA.INSTANCE.typed_value_into_boolean(this.consumePointer()); this.value = value == 0 ? false : true; - this.rawPointer = null; } return (Boolean) this.value; } @@ -97,8 +89,7 @@ public class TypedValue extends RustObject { */ public Double asDouble() { if (!this.isConsumed()) { - this.value = JNA.INSTANCE.typed_value_into_double(this.rawPointer); - this.rawPointer = null; + this.value = JNA.INSTANCE.typed_value_into_double(this.consumePointer()); } return (Double)value; } @@ -110,8 +101,7 @@ public class TypedValue extends RustObject { */ public Date asDate() { if (!this.isConsumed()) { - this.value = new Date(JNA.INSTANCE.typed_value_into_timestamp(this.rawPointer) * 1_000); - this.rawPointer = null; + this.value = new Date(JNA.INSTANCE.typed_value_into_timestamp(this.consumePointer()) * 1_000); } return (Date)this.value; } @@ -123,8 +113,8 @@ public class TypedValue extends RustObject { */ public String asString() { if (!this.isConsumed()) { - this.value = JNA.INSTANCE.typed_value_into_string(this.rawPointer); - this.rawPointer = null; + this.value = getAndConsumeMentatString( + JNA.INSTANCE.typed_value_into_string(this.consumePointer())); } return (String)value; } @@ -136,16 +126,13 @@ public class TypedValue extends RustObject { */ public UUID asUUID() { if (!this.isConsumed()) { - this.value = getUUIDFromPointer(JNA.INSTANCE.typed_value_into_uuid(this.rawPointer)); - this.rawPointer = null; + this.value = getAndConsumeUUIDPointer(JNA.INSTANCE.typed_value_into_uuid(this.consumePointer())); } return (UUID)this.value; } @Override - public void close() { - if (this.rawPointer != null) { - JNA.INSTANCE.typed_value_destroy(this.rawPointer); - } + protected void destroyPointer(JNA.TypedValue p) { + JNA.INSTANCE.typed_value_destroy(p); } } diff --git a/sdks/swift/Mentat/Mentat.xcodeproj/project.pbxproj b/sdks/swift/Mentat/Mentat.xcodeproj/project.pbxproj index dad9a961..cdb4c6f7 100644 --- a/sdks/swift/Mentat/Mentat.xcodeproj/project.pbxproj +++ b/sdks/swift/Mentat/Mentat.xcodeproj/project.pbxproj @@ -11,7 +11,7 @@ 7B64E44D209094520063909F /* InProgressBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B64E44A209094510063909F /* InProgressBuilder.swift */; }; 7B64E44E209094520063909F /* EntityBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B64E44B209094510063909F /* EntityBuilder.swift */; }; 7B64E44F209094520063909F /* InProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B64E44C209094520063909F /* InProgress.swift */; }; - 7B74483D208DF667006CFFB0 /* Result+Unwrap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B74483C208DF667006CFFB0 /* Result+Unwrap.swift */; }; + 7B74483D208DF667006CFFB0 /* RustError+Unwrap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B74483C208DF667006CFFB0 /* RustError+Unwrap.swift */; }; 7BAE75A42089022B00895D37 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 7BAE75A32089022B00895D37 /* libsqlite3.tbd */; }; 7BDB96942077C299009D0651 /* Mentat.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7BDB968A2077C299009D0651 /* Mentat.framework */; }; 7BDB96992077C299009D0651 /* MentatTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BDB96982077C299009D0651 /* MentatTests.swift */; }; @@ -29,6 +29,8 @@ 7BDB96C9207B735A009D0651 /* fixtures in Resources */ = {isa = PBXBuildFile; fileRef = 7BDB96C8207B735A009D0651 /* fixtures */; }; 7BDB96CC207B7684009D0651 /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BDB96CB207B7684009D0651 /* Errors.swift */; }; 7BEB7D2C207D03DA000369AD /* TxReport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BEB7D2B207D03DA000369AD /* TxReport.swift */; }; + D53A75B820D2DD3D00BAD7EA /* String+Destroying.swift in Sources */ = {isa = PBXBuildFile; fileRef = D53A75B720D2DD3D00BAD7EA /* String+Destroying.swift */; }; + D574353420DC26C7007C92EB /* UUID+Destroying.swift in Sources */ = {isa = PBXBuildFile; fileRef = D574353320DC26C7007C92EB /* UUID+Destroying.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -58,7 +60,7 @@ 7B64E44A209094510063909F /* InProgressBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InProgressBuilder.swift; sourceTree = ""; }; 7B64E44B209094510063909F /* EntityBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EntityBuilder.swift; sourceTree = ""; }; 7B64E44C209094520063909F /* InProgress.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InProgress.swift; sourceTree = ""; }; - 7B74483C208DF667006CFFB0 /* Result+Unwrap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "Result+Unwrap.swift"; path = "Mentat/Extensions/Result+Unwrap.swift"; sourceTree = SOURCE_ROOT; }; + 7B74483C208DF667006CFFB0 /* RustError+Unwrap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "RustError+Unwrap.swift"; path = "Mentat/Extensions/RustError+Unwrap.swift"; sourceTree = SOURCE_ROOT; }; 7BAE75A32089022B00895D37 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; }; 7BDB968A2077C299009D0651 /* Mentat.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Mentat.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7BDB968D2077C299009D0651 /* Mentat.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Mentat.h; sourceTree = ""; }; @@ -81,6 +83,8 @@ 7BDB96CB207B7684009D0651 /* Errors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = ""; }; 7BEB7D23207BE2AF000369AD /* libmentat_ffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libmentat_ffi.a; path = ../../../target/universal/release/libmentat_ffi.a; sourceTree = ""; }; 7BEB7D2B207D03DA000369AD /* TxReport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TxReport.swift; sourceTree = ""; }; + D53A75B720D2DD3D00BAD7EA /* String+Destroying.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Destroying.swift"; sourceTree = ""; }; + D574353320DC26C7007C92EB /* UUID+Destroying.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UUID+Destroying.swift"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -193,8 +197,10 @@ 7BDB96C42077D346009D0651 /* Extensions */ = { isa = PBXGroup; children = ( - 7B74483C208DF667006CFFB0 /* Result+Unwrap.swift */, + 7B74483C208DF667006CFFB0 /* RustError+Unwrap.swift */, + D574353320DC26C7007C92EB /* UUID+Destroying.swift */, 7BDB96C52077D346009D0651 /* Date+Int64.swift */, + D53A75B720D2DD3D00BAD7EA /* String+Destroying.swift */, ); path = Extensions; sourceTree = ""; @@ -331,6 +337,7 @@ buildActionMask = 2147483647; files = ( 7BDB96B32077C38E009D0651 /* RustObject.swift in Sources */, + D53A75B820D2DD3D00BAD7EA /* String+Destroying.swift in Sources */, 7BDB96C62077D347009D0651 /* Date+Int64.swift in Sources */, 7BEB7D2C207D03DA000369AD /* TxReport.swift in Sources */, 7BDB96B42077C38E009D0651 /* OptionalRustObject.swift in Sources */, @@ -339,10 +346,11 @@ 7BDB96CC207B7684009D0651 /* Errors.swift in Sources */, 7BDB96B02077C38E009D0651 /* Mentat.swift in Sources */, 7BDB96B72077C38E009D0651 /* TypedValue.swift in Sources */, + D574353420DC26C7007C92EB /* UUID+Destroying.swift in Sources */, 7B64E44E209094520063909F /* EntityBuilder.swift in Sources */, 7B64E44D209094520063909F /* InProgressBuilder.swift in Sources */, 7BDB96B52077C38E009D0651 /* TupleResult.swift in Sources */, - 7B74483D208DF667006CFFB0 /* Result+Unwrap.swift in Sources */, + 7B74483D208DF667006CFFB0 /* RustError+Unwrap.swift in Sources */, 7B64E44F209094520063909F /* InProgress.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/sdks/swift/Mentat/Mentat/Core/TypedValue.swift b/sdks/swift/Mentat/Mentat/Core/TypedValue.swift index 1963ec5f..7ec73788 100644 --- a/sdks/swift/Mentat/Mentat/Core/TypedValue.swift +++ b/sdks/swift/Mentat/Mentat/Core/TypedValue.swift @@ -82,7 +82,7 @@ open class TypedValue: OptionalRustObject { } if !self.isConsumed() { - self.value = String(cString: typed_value_into_kw(self.raw!)) + self.value = String(destroyingRustString: typed_value_into_kw(self.raw!)) } return self.value as! String } @@ -152,7 +152,7 @@ open class TypedValue: OptionalRustObject { } if !self.isConsumed() { - self.value = String(cString: typed_value_into_string(self.raw!)) + self.value = String(destroyingRustString: typed_value_into_string(self.raw!)); } return self.value as! String } @@ -169,8 +169,8 @@ open class TypedValue: OptionalRustObject { } if !self.isConsumed() { - let bytes = typed_value_into_uuid(self.raw!).pointee - self.value = UUID(uuid: bytes) + let bytes = typed_value_into_uuid(self.raw!); + self.value = UUID(destroyingRustUUID: bytes); } return self.value as! UUID? } diff --git a/sdks/swift/Mentat/Mentat/Extensions/Result+Unwrap.swift b/sdks/swift/Mentat/Mentat/Extensions/Result+Unwrap.swift deleted file mode 100644 index 7204067b..00000000 --- a/sdks/swift/Mentat/Mentat/Extensions/Result+Unwrap.swift +++ /dev/null @@ -1,50 +0,0 @@ -/* 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 - -public extension Result { - /** - Force unwraps a result. - Expects there to be a value attached and throws an error is there is not. - - - Throws: `ResultError.error` if the result contains an error - - Throws: `ResultError.empty` if the result contains no error but also no result. - - - Returns: The pointer to the successful result value. - */ - @discardableResult public func unwrap() throws -> UnsafeMutableRawPointer { - guard let success = self.ok else { - if let error = self.err { - throw ResultError.error(message: String(cString: error)) - } - throw ResultError.empty - } - return success - } - - /** - Unwraps an optional result, yielding either a successful value or a nil. - - - Throws: `ResultError.error` if the result contains an error - - - Returns: The pointer to the successful result value, or nil if no value is present. - */ - @discardableResult public func tryUnwrap() throws -> UnsafeMutableRawPointer? { - guard let success = self.ok else { - if let error = self.err { - throw ResultError.error(message: String(cString: error)) - } - return nil - } - return success - } -} diff --git a/sdks/swift/Mentat/Mentat/Extensions/RustError+Unwrap.swift b/sdks/swift/Mentat/Mentat/Extensions/RustError+Unwrap.swift new file mode 100644 index 00000000..90825dc8 --- /dev/null +++ b/sdks/swift/Mentat/Mentat/Extensions/RustError+Unwrap.swift @@ -0,0 +1,48 @@ +/* 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 + +extension RustError { + + @discardableResult + public static func unwrap(_ callback: (UnsafeMutablePointer) throws -> OpaquePointer?) throws -> OpaquePointer { + var err = RustError(message: nil) + guard let result = try callback(&err) else { + if let message = err.message { + throw ResultError.error(message: String(destroyingRustString: message)) + } + throw ResultError.empty + } + return result; + } + + @discardableResult + public static func tryUnwrap(_ callback: (UnsafeMutablePointer) throws -> OpaquePointer?) throws -> OpaquePointer? { + var err = RustError(message: nil) + guard let result = try callback(&err) else { + if let message = err.message { + throw ResultError.error(message: String(destroyingRustString: message)) + } + return nil + } + return result; + } + + public static func withErrorCheck(_ callback: (UnsafeMutablePointer) throws -> Void) throws { + var err = RustError(message: nil) + try callback(&err); + if let message = err.message { + throw ResultError.error(message: String(destroyingRustString: message)) + } + } +} + diff --git a/sdks/swift/Mentat/Mentat/Extensions/String+Destroying.swift b/sdks/swift/Mentat/Mentat/Extensions/String+Destroying.swift new file mode 100644 index 00000000..5d8c0729 --- /dev/null +++ b/sdks/swift/Mentat/Mentat/Extensions/String+Destroying.swift @@ -0,0 +1,21 @@ +/* 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 + +public extension String { + /** Helper to construct a String from a Rust string without leaking it. */ + public init(destroyingRustString rustCString: UnsafeMutablePointer) { + defer { rust_c_string_destroy(rustCString); } + self.init(cString: rustCString) + } +} diff --git a/sdks/swift/Mentat/Mentat/Extensions/UUID+Destroying.swift b/sdks/swift/Mentat/Mentat/Extensions/UUID+Destroying.swift new file mode 100644 index 00000000..e6c47b33 --- /dev/null +++ b/sdks/swift/Mentat/Mentat/Extensions/UUID+Destroying.swift @@ -0,0 +1,21 @@ +/* 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 + +public extension UUID { + /** Helper to construct a UUID from a Rust [u8; 16] without leaking it. */ + public init(destroyingRustUUID rawUUID: UnsafeMutablePointer) { + defer { uuid_destroy(rawUUID); } + self.init(uuid: rawUUID.pointee) + } +} diff --git a/sdks/swift/Mentat/Mentat/Mentat.swift b/sdks/swift/Mentat/Mentat/Mentat.swift index d46450b6..2bd569e6 100644 --- a/sdks/swift/Mentat/Mentat/Mentat.swift +++ b/sdks/swift/Mentat/Mentat/Mentat.swift @@ -77,14 +77,16 @@ open class Mentat: RustObject { - Throws: `ResultError.error` if an error occured while trying to cache the attribute. */ open 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() - } + try RustError.withErrorCheck({err in + switch direction { + case .forward: + store_cache_attribute_forward(self.raw, attribute, err) + case .reverse: + store_cache_attribute_reverse(self.raw, attribute, err) + case .both: + store_cache_attribute_bi_directional(self.raw, attribute, err) + } + }); } /** @@ -96,8 +98,7 @@ open class Mentat: RustObject { - Returns: The `TxReport` of the completed transaction */ open func transact(transaction: String) throws -> TxReport { - let result = store_transact(self.raw, transaction).pointee - return TxReport(raw: try result.unwrap()) + return TxReport(raw: try RustError.unwrap({err in store_transact(self.raw, transaction, err) })) } /** @@ -109,8 +110,7 @@ open class Mentat: RustObject { - Returns: The `InProgress` used to manage the transaction */ open func beginTransaction() throws -> InProgress { - let result = store_begin_transaction(self.raw).pointee; - return InProgress(raw: try result.unwrap()) + return InProgress(raw: try RustError.unwrap({err in store_begin_transaction(self.raw, err) })); } /** @@ -122,8 +122,7 @@ open class Mentat: RustObject { - Returns: an `InProgressBuilder` for this `InProgress` */ open func entityBuilder() throws -> InProgressBuilder { - let result = store_in_progress_builder(self.raw).pointee - return InProgressBuilder(raw: try result.unwrap()) + return InProgressBuilder(raw: try RustError.unwrap({err in store_in_progress_builder(self.raw, err) })) } /** @@ -138,8 +137,8 @@ open class Mentat: RustObject { - Returns: an `EntityBuilder` for this `InProgress` */ open func entityBuilder(forEntid entid: Entid) throws -> EntityBuilder { - let result = store_entity_builder_from_entid(self.raw, entid).pointee - return EntityBuilder(raw: try result.unwrap()) + return EntityBuilder(raw: try RustError.unwrap({err in + store_entity_builder_from_entid(self.raw, entid, err) })) } /** @@ -154,8 +153,9 @@ open class Mentat: RustObject { - Returns: an `EntityBuilder` for this `InProgress` */ open 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()) + return EntityBuilder(raw: try RustError.unwrap({err in + store_entity_builder_from_temp_id(self.raw, tempId, err) + })) } /** @@ -189,8 +189,9 @@ open class Mentat: RustObject { - Returns: The `TypedValue` containing the value of the attribute for the entity. */ open func value(forAttribute attribute: String, ofEntity entid: Entid) throws -> TypedValue? { - let result = store_value_for_attribute(self.raw, entid, attribute).pointee - return TypedValue(raw: try result.unwrap()) + return TypedValue(raw: try RustError.unwrap({err in + store_value_for_attribute(self.raw, entid, attribute, err) + })); } // Destroys the pointer by passing it back into Rust to be cleaned up diff --git a/sdks/swift/Mentat/Mentat/Query/Query.swift b/sdks/swift/Mentat/Mentat/Query/Query.swift index 765f50eb..480821ba 100644 --- a/sdks/swift/Mentat/Mentat/Query/Query.swift +++ b/sdks/swift/Mentat/Mentat/Query/Query.swift @@ -245,14 +245,15 @@ open class Query: OptionalRustObject { - Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the query has previously been executed. */ open func run(callback: @escaping (RelResult?) -> Void) throws { - let result = query_builder_execute(try! self.validPointer()) + var error = RustError(message: nil) + let result = query_builder_execute(try! self.validPointer(), &error); self.raw = nil - if let err = result.pointee.err { - let message = String(cString: err) + if let err = error.message { + let message = String(destroyingRustString: err) throw QueryError.executionFailed(message: message) } - guard let results = result.pointee.ok else { + guard let results = result else { callback(nil) return } @@ -269,18 +270,19 @@ open class Query: OptionalRustObject { - Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the query has previously been executed. */ open func runScalar(callback: @escaping (TypedValue?) -> Void) throws { - let result = query_builder_execute_scalar(try! self.validPointer()) + var error = RustError(message: nil) + let result = query_builder_execute_scalar(try! self.validPointer(), &error) self.raw = nil - if let err = result.pointee.err { - let message = String(cString: err) + if let err = error.message { + let message = String(destroyingRustString: err) throw QueryError.executionFailed(message: message) } - guard let results = result.pointee.ok else { + guard let results = result else { callback(nil) return } - callback(TypedValue(raw: OpaquePointer(results))) + callback(TypedValue(raw: results)) } /** @@ -293,14 +295,15 @@ open class Query: OptionalRustObject { - Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the query has previously been executed. */ open func runColl(callback: @escaping (ColResult?) -> Void) throws { - let result = query_builder_execute_coll(try! self.validPointer()) + var error = RustError(message: nil) + let result = query_builder_execute_coll(try! self.validPointer(), &error) self.raw = nil - if let err = result.pointee.err { - let message = String(cString: err) - throw QueryError.executionFailed(message: message) + if let err = error.message { + let message = String(destroyingRustString: err) + throw QueryError.executionFailed(message: message) } - guard let results = result.pointee.ok else { + guard let results = result else { callback(nil) return } @@ -317,18 +320,19 @@ open class Query: OptionalRustObject { - Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the query has previously been executed. */ open func runTuple(callback: @escaping (TupleResult?) -> Void) throws { - let result = query_builder_execute_tuple(try! self.validPointer()) + var error = RustError(message: nil) + let result = query_builder_execute_tuple(try! self.validPointer(), &error) self.raw = nil - if let err = result.pointee.err { - let message = String(cString: err) + if let err = error.message { + let message = String(destroyingRustString: err) throw QueryError.executionFailed(message: message) } - guard let results = result.pointee.ok else { + guard let results = result else { callback(nil) return } - callback(TupleResult(raw: OpaquePointer(results))) + callback(TupleResult(raw: results)) } override open func cleanup(pointer: OpaquePointer) { diff --git a/sdks/swift/Mentat/Mentat/Query/RelResult.swift b/sdks/swift/Mentat/Mentat/Query/RelResult.swift index a5f606b7..7b72b186 100644 --- a/sdks/swift/Mentat/Mentat/Query/RelResult.swift +++ b/sdks/swift/Mentat/Mentat/Query/RelResult.swift @@ -55,7 +55,7 @@ open class RelResult: OptionalRustObject { } override open func cleanup(pointer: OpaquePointer) { - destroy(UnsafeMutableRawPointer(pointer)) + typed_value_result_set_destroy(pointer) } } diff --git a/sdks/swift/Mentat/Mentat/Query/TupleResult.swift b/sdks/swift/Mentat/Mentat/Query/TupleResult.swift index 00c1cb2a..d41a4f92 100644 --- a/sdks/swift/Mentat/Mentat/Query/TupleResult.swift +++ b/sdks/swift/Mentat/Mentat/Query/TupleResult.swift @@ -77,7 +77,8 @@ open class TupleResult: OptionalRustObject { - Returns: The keyword `String` at that index. */ open func asKeyword(index: Int) -> String { - return String(cString: value_at_index_into_kw(self.raw!, Int32(index))) + let str = value_at_index_into_kw(self.raw!, Int32(index)); + return String(destroyingRustString: str); } /** @@ -129,7 +130,8 @@ open class TupleResult: OptionalRustObject { - Returns: The `String` at that index. */ open func asString(index: Int) -> String { - return String(cString: value_at_index_into_string(self.raw!, Int32(index))) + let str = value_at_index_into_string(self.raw!, Int32(index)); + return String(destroyingRustString: str) } /** @@ -142,7 +144,8 @@ open class TupleResult: OptionalRustObject { - Returns: The `UUID` at that index. */ open func asUUID(index: Int) -> UUID? { - return UUID(uuid: value_at_index_into_uuid(self.raw!, Int32(index)).pointee) + let uuid = value_at_index_into_uuid(self.raw!, Int32(index)); + return UUID(destroyingRustUUID: uuid) } override open func cleanup(pointer: OpaquePointer) { diff --git a/sdks/swift/Mentat/Mentat/Rust/RustObject.swift b/sdks/swift/Mentat/Mentat/Rust/RustObject.swift index 64252f04..80fea717 100644 --- a/sdks/swift/Mentat/Mentat/Rust/RustObject.swift +++ b/sdks/swift/Mentat/Mentat/Rust/RustObject.swift @@ -38,7 +38,7 @@ open class RustObject: Destroyable { } self.raw = r } - + public func getRaw() -> OpaquePointer { return self.raw } @@ -46,7 +46,7 @@ open class RustObject: Destroyable { deinit { self.cleanup(pointer: self.raw) } - + open func cleanup(pointer: OpaquePointer) { fatalError("\(cleanup) is not implemented.") } diff --git a/sdks/swift/Mentat/Mentat/Transact/EntityBuilder.swift b/sdks/swift/Mentat/Mentat/Transact/EntityBuilder.swift index 750afc7c..bfb15a88 100644 --- a/sdks/swift/Mentat/Mentat/Transact/EntityBuilder.swift +++ b/sdks/swift/Mentat/Mentat/Transact/EntityBuilder.swift @@ -69,7 +69,9 @@ open class EntityBuilder: OptionalRustObject { is not `:db.type/long`. */ open func add(keyword: String, long value: Int64) throws { - try entity_builder_add_long(try self.validPointer(), keyword, value).pointee.tryUnwrap() + try RustError.withErrorCheck({ err in + entity_builder_add_long(try self.validPointer(), keyword, value, err) + }) } /** @@ -84,7 +86,9 @@ open class EntityBuilder: OptionalRustObject { is not `:db.type/ref`. */ open func add(keyword: String, reference value: Int64) throws { - try entity_builder_add_ref(try self.validPointer(), keyword, value).pointee.tryUnwrap() + try RustError.withErrorCheck({ err in + entity_builder_add_ref(try self.validPointer(), keyword, value, err) + }) } /** @@ -99,7 +103,9 @@ open class EntityBuilder: OptionalRustObject { is not `:db.type/keyword`. */ open func add(keyword: String, keyword value: String) throws { - try entity_builder_add_keyword(try self.validPointer(), keyword, value).pointee.tryUnwrap() + try RustError.withErrorCheck({ err in + entity_builder_add_keyword(try self.validPointer(), keyword, value, err) + }) } /** @@ -114,7 +120,9 @@ open class EntityBuilder: OptionalRustObject { is not `:db.type/boolean`. */ open func add(keyword: String, boolean value: Bool) throws { - try entity_builder_add_boolean(try self.validPointer(), keyword, value ? 1 : 0).pointee.tryUnwrap() + try RustError.withErrorCheck({ err in + entity_builder_add_boolean(try self.validPointer(), keyword, value ? 1 : 0, err) + }) } /** @@ -129,7 +137,9 @@ open class EntityBuilder: OptionalRustObject { is not `:db.type/double`. */ open func add(keyword: String, double value: Double) throws { - try entity_builder_add_double(try self.validPointer(), keyword, value).pointee.tryUnwrap() + try RustError.withErrorCheck({ err in + entity_builder_add_double(try self.validPointer(), keyword, value, err) + }) } /** @@ -145,7 +155,9 @@ open class EntityBuilder: OptionalRustObject { is not `:db.type/instant`. */ open func add(keyword: String, date value: Date) throws { - try entity_builder_add_timestamp(try self.validPointer(), keyword, value.toMicroseconds()).pointee.tryUnwrap() + try RustError.withErrorCheck({ err in + entity_builder_add_timestamp(try self.validPointer(), keyword, value.toMicroseconds(), err) + }) } /** @@ -160,7 +172,9 @@ open class EntityBuilder: OptionalRustObject { is not `:db.type/string`. */ open func add(keyword: String, string value: String) throws { - try entity_builder_add_string(try self.validPointer(), keyword, value).pointee.tryUnwrap() + try RustError.withErrorCheck({ err in + entity_builder_add_string(try self.validPointer(), keyword, value, err) + }) } /** @@ -177,7 +191,9 @@ open class EntityBuilder: OptionalRustObject { open 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() + try RustError.withErrorCheck({ err in + entity_builder_add_uuid(try self.validPointer(), keyword, uuidPtr, err) + }) } } @@ -193,7 +209,9 @@ open class EntityBuilder: OptionalRustObject { is not `:db.type/long`. */ open func retract(keyword: String, long value: Int64) throws { - try entity_builder_retract_long(try self.validPointer(), keyword, value).pointee.tryUnwrap() + try RustError.withErrorCheck({ err in + entity_builder_retract_long(try self.validPointer(), keyword, value, err) + }) } /** @@ -208,7 +226,9 @@ open class EntityBuilder: OptionalRustObject { is not `:db.type/ref`. */ open func retract(keyword: String, reference value: Int64) throws { - try entity_builder_retract_ref(try self.validPointer(), keyword, value).pointee.tryUnwrap() + try RustError.withErrorCheck({ err in + entity_builder_retract_ref(try self.validPointer(), keyword, value, err) + }) } /** @@ -223,7 +243,9 @@ open class EntityBuilder: OptionalRustObject { is not `:db.type/keyword`. */ open func retract(keyword: String, keyword value: String) throws { - try entity_builder_retract_keyword(try self.validPointer(), keyword, value).pointee.tryUnwrap() + try RustError.withErrorCheck({ err in + entity_builder_retract_keyword(try self.validPointer(), keyword, value, err) + }) } /** @@ -238,7 +260,9 @@ open class EntityBuilder: OptionalRustObject { is not `:db.type/boolean`. */ open func retract(keyword: String, boolean value: Bool) throws { - try entity_builder_retract_boolean(try self.validPointer(), keyword, value ? 1 : 0).pointee.tryUnwrap() + try RustError.withErrorCheck({ err in + entity_builder_retract_boolean(try self.validPointer(), keyword, value ? 1 : 0, err) + }) } /** @@ -253,7 +277,9 @@ open class EntityBuilder: OptionalRustObject { is not `:db.type/double`. */ open func retract(keyword: String, double value: Double) throws { - try entity_builder_retract_double(try self.validPointer(), keyword, value).pointee.tryUnwrap() + try RustError.withErrorCheck({ err in + entity_builder_retract_double(try self.validPointer(), keyword, value, err) + }) } /** @@ -268,7 +294,9 @@ open class EntityBuilder: OptionalRustObject { is not `:db.type/instant`. */ open func retract(keyword: String, date value: Date) throws { - try entity_builder_retract_timestamp(try self.validPointer(), keyword, value.toMicroseconds()).pointee.tryUnwrap() + try RustError.withErrorCheck({ err in + entity_builder_retract_timestamp(try self.validPointer(), keyword, value.toMicroseconds(), err) + }) } /** @@ -283,7 +311,9 @@ open class EntityBuilder: OptionalRustObject { is not `:db.type/string`. */ open func retract(keyword: String, string value: String) throws { - try entity_builder_retract_string(try self.validPointer(), keyword, value).pointee.tryUnwrap() + try RustError.withErrorCheck({ err in + entity_builder_retract_string(try self.validPointer(), keyword, value, err) + }) } @@ -301,7 +331,9 @@ open class EntityBuilder: OptionalRustObject { open 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() + try RustError.withErrorCheck({ err in + entity_builder_retract_uuid(try self.validPointer(), keyword, uuidPtr, err) + }) } } @@ -323,9 +355,12 @@ open class EntityBuilder: OptionalRustObject { defer { self.raw = nil } - let result = entity_builder_transact(try self.validPointer()).pointee + let result = entity_builder_transact(try self.validPointer()); let inProgress = InProgress(raw: result.inProgress) - guard let report = try result.result.pointee.tryUnwrap() else { + if let errorMessage = result.error.message { + throw ResultError.error(message: String(destroyingRustString: errorMessage)) + } + guard let report = result.txReport else { return (inProgress, nil) } return (inProgress, TxReport(raw: report)) @@ -346,9 +381,11 @@ open class EntityBuilder: OptionalRustObject { defer { self.raw = nil } - return TxReport(raw: try entity_builder_commit(try self.validPointer()).pointee.unwrap()) + return TxReport(raw: try RustError.unwrap({ err in + entity_builder_commit(try self.validPointer(), err) + })) } - + override open func cleanup(pointer: OpaquePointer) { entity_builder_destroy(pointer) } diff --git a/sdks/swift/Mentat/Mentat/Transact/InProgress.swift b/sdks/swift/Mentat/Mentat/Transact/InProgress.swift index bb006162..81e4c8af 100644 --- a/sdks/swift/Mentat/Mentat/Transact/InProgress.swift +++ b/sdks/swift/Mentat/Mentat/Transact/InProgress.swift @@ -126,7 +126,10 @@ open class InProgress: OptionalRustObject { defer { self.raw = nil } - return EntityBuilder(raw: in_progress_entity_builder_from_temp_id(try self.validPointer(), tempId)) + let builder = try RustError.unwrap({ err in + in_progress_entity_builder_from_temp_id(try self.validPointer(), tempId, err) + }) + return EntityBuilder(raw: builder) } /** @@ -144,8 +147,10 @@ open class InProgress: OptionalRustObject { - Returns: The `TxReport` generated by the transact. */ open func transact(transaction: String) throws -> TxReport { - let result = in_progress_transact(try self.validPointer(), transaction).pointee - return TxReport(raw: try result.unwrap()) + let report = try RustError.unwrap({ err in + in_progress_transact(try self.validPointer(), transaction, err) + }) + return TxReport(raw: report) } /** @@ -160,7 +165,9 @@ open class InProgress: OptionalRustObject { defer { self.raw = nil } - try in_progress_commit(try self.validPointer()).pointee.tryUnwrap() + try RustError.withErrorCheck({ err in + in_progress_commit(try self.validPointer(), err) + }) } /** @@ -175,7 +182,9 @@ open class InProgress: OptionalRustObject { defer { self.raw = nil } - try in_progress_rollback(try self.validPointer()).pointee.tryUnwrap() + try RustError.withErrorCheck({ err in + in_progress_rollback(try self.validPointer(), err) + }) } override open func cleanup(pointer: OpaquePointer) { diff --git a/sdks/swift/Mentat/Mentat/Transact/InProgressBuilder.swift b/sdks/swift/Mentat/Mentat/Transact/InProgressBuilder.swift index 8a4ce085..04c4c6c1 100644 --- a/sdks/swift/Mentat/Mentat/Transact/InProgressBuilder.swift +++ b/sdks/swift/Mentat/Mentat/Transact/InProgressBuilder.swift @@ -73,7 +73,9 @@ open class InProgressBuilder: OptionalRustObject { is not `:db.type/long`. */ open func add(entid: Entid, keyword: String, long value: Int64) throws { - try in_progress_builder_add_long(try self.validPointer(), entid, keyword, value).pointee.tryUnwrap() + try RustError.withErrorCheck({ err in + in_progress_builder_add_long(try self.validPointer(), entid, keyword, value, err) + }) } /** @@ -89,7 +91,9 @@ open class InProgressBuilder: OptionalRustObject { is not `:db.type/ref`. */ open func add(entid: Entid, keyword: String, reference value: Entid) throws { - try in_progress_builder_add_ref(try self.validPointer(), entid, keyword, value).pointee.tryUnwrap() + try RustError.withErrorCheck({ err in + in_progress_builder_add_ref(try self.validPointer(), entid, keyword, value, err) + }) } /** @@ -105,7 +109,9 @@ open class InProgressBuilder: OptionalRustObject { is not `:db.type/keyword`. */ open func add(entid: Entid, keyword: String, keyword value: String) throws { - try in_progress_builder_add_keyword(try self.validPointer(), entid, keyword, value).pointee.tryUnwrap() + try RustError.withErrorCheck({ err in + in_progress_builder_add_keyword(try self.validPointer(), entid, keyword, value, err) + }) } /** @@ -121,7 +127,9 @@ open class InProgressBuilder: OptionalRustObject { is not `:db.type/boolean`. */ open 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() + try RustError.withErrorCheck({ err in + in_progress_builder_add_boolean(try self.validPointer(), entid, keyword, value ? 1 : 0, err) + }) } /** @@ -137,7 +145,9 @@ open class InProgressBuilder: OptionalRustObject { is not `:db.type/double`. */ open func add(entid: Entid, keyword: String, double value: Double) throws { - try in_progress_builder_add_double(try self.validPointer(), entid, keyword, value).pointee.tryUnwrap() + try RustError.withErrorCheck({ err in + in_progress_builder_add_double(try self.validPointer(), entid, keyword, value, err) + }) } /** @@ -153,7 +163,9 @@ open class InProgressBuilder: OptionalRustObject { is not `:db.type/instant`. */ open 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() + try RustError.withErrorCheck({ err in + in_progress_builder_add_timestamp(try self.validPointer(), entid, keyword, value.toMicroseconds(), err) + }) } /** @@ -169,7 +181,9 @@ open class InProgressBuilder: OptionalRustObject { is not `:db.type/string`. */ open func add(entid: Entid, keyword: String, string value: String) throws { - try in_progress_builder_add_string(try self.validPointer(), entid, keyword, value).pointee.tryUnwrap() + try RustError.withErrorCheck({ err in + in_progress_builder_add_string(try self.validPointer(), entid, keyword, value, err) + }) } /** @@ -187,7 +201,9 @@ open class InProgressBuilder: OptionalRustObject { open 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() + try RustError.withErrorCheck({ err in + in_progress_builder_add_uuid(try self.validPointer(), entid, keyword, uuidPtr, err) + }) } } @@ -204,7 +220,9 @@ open class InProgressBuilder: OptionalRustObject { is not `:db.type/long`. */ open func retract(entid: Entid, keyword: String, long value: Int64) throws { - try in_progress_builder_retract_long(try self.validPointer(), entid, keyword, value).pointee.tryUnwrap() + try RustError.withErrorCheck({ err in + in_progress_builder_retract_long(try self.validPointer(), entid, keyword, value, err) + }) } /** @@ -220,7 +238,9 @@ open class InProgressBuilder: OptionalRustObject { is not `:db.type/ref`. */ open func retract(entid: Entid, keyword: String, reference value: Entid) throws { - try in_progress_builder_retract_ref(try self.validPointer(), entid, keyword, value).pointee.tryUnwrap() + try RustError.withErrorCheck({ err in + in_progress_builder_retract_ref(try self.validPointer(), entid, keyword, value, err) + }) } /** @@ -236,7 +256,9 @@ open class InProgressBuilder: OptionalRustObject { is not `:db.type/keyword`. */ open func retract(entid: Entid, keyword: String, keyword value: String) throws { - try in_progress_builder_retract_keyword(try self.validPointer(), entid, keyword, value).pointee.tryUnwrap() + try RustError.withErrorCheck({ err in + in_progress_builder_retract_keyword(try self.validPointer(), entid, keyword, value, err) + }) } /** @@ -252,7 +274,9 @@ open class InProgressBuilder: OptionalRustObject { is not `:db.type/boolean`. */ open 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() + try RustError.withErrorCheck({ err in + in_progress_builder_retract_boolean(try self.validPointer(), entid, keyword, value ? 1 : 0, err) + }) } /** @@ -268,7 +292,9 @@ open class InProgressBuilder: OptionalRustObject { is not `:db.type/double`. */ open func retract(entid: Entid, keyword: String, double value: Double) throws { - try in_progress_builder_retract_double(try self.validPointer(), entid, keyword, value).pointee.tryUnwrap() + try RustError.withErrorCheck({ err in + in_progress_builder_retract_double(try self.validPointer(), entid, keyword, value, err) + }) } /** @@ -284,7 +310,9 @@ open class InProgressBuilder: OptionalRustObject { is not `:db.type/instant`. */ open 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() + try RustError.withErrorCheck({ err in + in_progress_builder_retract_timestamp(try self.validPointer(), entid, keyword, value.toMicroseconds(), err) + }) } /** @@ -300,7 +328,9 @@ open class InProgressBuilder: OptionalRustObject { is not `:db.type/string`. */ open func retract(entid: Entid, keyword: String, string value: String) throws { - try in_progress_builder_retract_string(try self.validPointer(), entid, keyword, value).pointee.tryUnwrap() + try RustError.withErrorCheck({ err in + in_progress_builder_retract_string(try self.validPointer(), entid, keyword, value, err) + }) } /** @@ -318,7 +348,9 @@ open class InProgressBuilder: OptionalRustObject { open 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() + try RustError.withErrorCheck({ err in + in_progress_builder_retract_uuid(try self.validPointer(), entid, keyword, uuidPtr, err) + }) } } @@ -340,9 +372,12 @@ open class InProgressBuilder: OptionalRustObject { 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 { + let result = in_progress_builder_transact(try self.validPointer()) + if let errorMessage = result.error.message { + throw ResultError.error(message: String(destroyingRustString: errorMessage)) + } + let inProgress = InProgress(raw: result.inProgress); + guard let report = result.txReport else { return (inProgress, nil) } return (inProgress, TxReport(raw: report)) @@ -363,7 +398,9 @@ open class InProgressBuilder: OptionalRustObject { defer { self.raw = nil } - return TxReport(raw: try in_progress_builder_commit(try self.validPointer()).pointee.unwrap()) + return TxReport(raw: try RustError.unwrap({err in + in_progress_builder_commit(try self.validPointer(), err) + })); } override open func cleanup(pointer: OpaquePointer) { diff --git a/sdks/swift/Mentat/Mentat/Transact/TxReport.swift b/sdks/swift/Mentat/Mentat/Transact/TxReport.swift index 841be07b..fa0ed121 100644 --- a/sdks/swift/Mentat/Mentat/Transact/TxReport.swift +++ b/sdks/swift/Mentat/Mentat/Transact/TxReport.swift @@ -52,7 +52,9 @@ open class TxReport: RustObject { guard let entidPtr = tx_report_entity_for_temp_id(self.raw, tempId) else { return nil } - return entidPtr.pointee + let entid = entidPtr.pointee; + destroy(entidPtr); + return entid } override open func cleanup(pointer: OpaquePointer) { diff --git a/sdks/swift/Mentat/Mentat/store.h b/sdks/swift/Mentat/Mentat/store.h index e8ae9af9..4163358b 100644 --- a/sdks/swift/Mentat/Mentat/store.h +++ b/sdks/swift/Mentat/Mentat/store.h @@ -21,52 +21,69 @@ * macros and flags that will not be recognised by other C based languages. */ + +// Opaque Structs mapping to Rust types that are passed over the FFI boundary. In cases where the +// struct's name differs from the name used for the Rust type, it's noted in a comment. +struct EntityBuilder; // Note: a `mentat::EntityBuilder>` +struct InProgress; +struct InProgressBuilder; +struct Query; // Note: a `mentat::QueryBuilder` +struct QueryResultRow; // Note: a `Vec` +struct QueryResultRows; // Note: a `mentat::RelResult` +struct QueryRowsIterator; // Note: a `mentat::BindingListIterator` +struct QueryRowIterator; // Note: a `mentat::BindingIterator` +struct Store; +struct TxReport; +struct TypedValue; // Note: a `mentat::Binding` + + /* - A mapping of the TxChange repr(C) Rust object. - The memory for this is managed by Swift. + A mapping of the TransactionChange repr(C) Rust object. + The memory for this is managed by Rust. */ struct TxChange { int64_t txid; - int64_t*_Nonnull* _Nonnull changes; + const int64_t* _Nonnull changes; uint64_t len; }; /* A mapping of the TxChangeList repr(C) Rust object. - The memory for this is managed by Swift. + The memory for this is managed by Rust. */ struct TxChangeList { - struct TxChange*_Nonnull* _Nonnull reports; + const struct TxChange* _Nonnull reports; uint64_t len; }; typedef struct TxChangeList TxChangeList; -/* - A mapping of the ExternResult repr(C) Rust object. - The memory for this is managed by Swift. - */ -struct Result { - void* _Nullable ok; - char* _Nullable err; +/* Representation of the `ExternError` Rust type. + + If `message` is not null, an error occur occurred (and we're responsible for freeing `message`, + using `rust_c_string_destroy`). +*/ +struct RustError { + char *message; }; -typedef struct Result Result; /* - A mapping of the ExternOption repr(C) Rust object. - The memory for this is managed by Swift. + A mapping of the ExternResult<()> repr(C) Rust object. + These are not allocated on the heap, but the memory for `ok` and `err` + is managed by Swift. */ -struct Option { - void* _Nullable value; -}; -typedef struct Option Option; +struct VoidResult { void* _Nullable ok; char* _Nullable err; }; +#define DEFINE_RESULT(Name, Type) struct Name { struct Type *_Nullable ok; char *_Nullable err; } /* A mapping of the InProgressTransactResult repr(C) Rust object. - The memory for this is managed by Swift. + These are not allocated on the heap, but the memory for `inProgress`, + `txReport`, and `result.message` (if pressent) + as well as `result.ok` and `result.err`, are managed by Swift. */ struct InProgressTransactResult { - struct InProgress*_Nonnull inProgress; - struct Result*_Nonnull result; + struct InProgress *_Nonnull inProgress; + struct TxReport *_Nullable txReport; + struct RustError error; }; typedef struct InProgressTransactResult InProgressTransactResult; @@ -84,24 +101,12 @@ typedef NS_ENUM(NSInteger, ValueType) { ValueTypeUuid }; -// Opaque Structs mapping to Rust types that are passed over the FFI boundary -struct EntityBuilder; -struct InProgress; -struct InProgressBuilder; -struct Query; -struct QueryResultRow; -struct QueryResultRows; -struct QueryRowsIterator; -struct QueryRowIterator; -struct Store; -struct TxReport; -struct TypedValue; - // Store struct Store*_Nonnull store_open(const char*_Nonnull uri); // Destructors. void destroy(void* _Nullable obj); +void uuid_destroy(uuid_t* _Nullable obj); void query_builder_destroy(struct Query* _Nullable obj); void store_destroy(struct Store* _Nonnull obj); void tx_report_destroy(struct TxReport* _Nonnull obj); @@ -113,72 +118,73 @@ 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); - +void rust_c_string_destroy(char *_Nullable s); // 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); +void store_cache_attribute_forward(struct Store*_Nonnull store, const char* _Nonnull attribute, struct RustError* _Nonnull error); +void store_cache_attribute_reverse(struct Store*_Nonnull store, const char* _Nonnull attribute, struct RustError* _Nonnull error); +void store_cache_attribute_bi_directional(struct Store*_Nonnull store, const char* _Nonnull attribute, struct RustError* _Nonnull error); // 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); +struct TxReport*_Nullable store_transact(struct Store*_Nonnull store, const char* _Nonnull transaction, struct RustError* _Nonnull error); +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); +struct InProgress *_Nullable store_begin_transaction(struct Store*_Nonnull store, struct RustError* _Nonnull error); // 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); +struct TxReport*_Nullable in_progress_transact(struct InProgress*_Nonnull in_progress, const char* _Nonnull transaction, struct RustError*_Nonnull err); +void in_progress_commit(struct InProgress*_Nonnull in_progress, struct RustError* _Nonnull error); +void in_progress_rollback(struct InProgress*_Nonnull in_progress, struct RustError* _Nonnull error); // in_progress entity building -struct Result*_Nonnull store_in_progress_builder(struct Store*_Nonnull store); +struct InProgressBuilder*_Nullable store_in_progress_builder(struct Store*_Nonnull store, struct RustError* _Nonnull error); 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_temp_id(struct InProgress*_Nonnull in_progress, const char*_Nonnull temp_id, struct RustError* _Nonnull error); 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); + +void in_progress_builder_add_string(struct InProgressBuilder*_Nonnull builder, const int64_t entid, const char*_Nonnull kw, const char*_Nonnull value, struct RustError* _Nonnull error); +void in_progress_builder_add_long(struct InProgressBuilder*_Nonnull builder, const int64_t entid, const char*_Nonnull kw, const int64_t value, struct RustError* _Nonnull error); +void in_progress_builder_add_ref(struct InProgressBuilder*_Nonnull builder, const int64_t entid, const char*_Nonnull kw, const int64_t value, struct RustError* _Nonnull error); +void in_progress_builder_add_keyword(struct InProgressBuilder*_Nonnull builder, const int64_t entid, const char*_Nonnull kw, const char*_Nonnull value, struct RustError* _Nonnull error); +void in_progress_builder_add_timestamp(struct InProgressBuilder*_Nonnull builder, const int64_t entid, const char*_Nonnull kw, const int64_t value, struct RustError* _Nonnull error); +void in_progress_builder_add_boolean(struct InProgressBuilder*_Nonnull builder, const int64_t entid, const char*_Nonnull kw, const int32_t value, struct RustError* _Nonnull error); +void in_progress_builder_add_double(struct InProgressBuilder*_Nonnull builder, const int64_t entid, const char*_Nonnull kw, const double value, struct RustError* _Nonnull error); +void in_progress_builder_add_uuid(struct InProgressBuilder*_Nonnull builder, const int64_t entid, const char*_Nonnull kw, const uuid_t* _Nonnull value, struct RustError* _Nonnull error); +void in_progress_builder_retract_string(struct InProgressBuilder*_Nonnull builder, const int64_t entid, const char*_Nonnull kw, const char*_Nonnull value, struct RustError* _Nonnull error); +void in_progress_builder_retract_long(struct InProgressBuilder*_Nonnull builder, const int64_t entid, const char*_Nonnull kw, const int64_t value, struct RustError* _Nonnull error); +void in_progress_builder_retract_ref(struct InProgressBuilder*_Nonnull builder, const int64_t entid, const char*_Nonnull kw, const int64_t value, struct RustError* _Nonnull error); +void in_progress_builder_retract_keyword(struct InProgressBuilder*_Nonnull builder, const int64_t entid, const char*_Nonnull kw, const char*_Nonnull value, struct RustError* _Nonnull error); +void in_progress_builder_retract_timestamp(struct InProgressBuilder*_Nonnull builder, const int64_t entid, const char*_Nonnull kw, const int64_t value, struct RustError* _Nonnull error); +void in_progress_builder_retract_boolean(struct InProgressBuilder*_Nonnull builder, const int64_t entid, const char*_Nonnull kw, const int32_t value, struct RustError* _Nonnull error); +void in_progress_builder_retract_double(struct InProgressBuilder*_Nonnull builder, const int64_t entid, const char*_Nonnull kw, const double value, struct RustError* _Nonnull error); +void in_progress_builder_retract_uuid(struct InProgressBuilder*_Nonnull builder, const int64_t entid, const char*_Nonnull kw, const uuid_t* _Nonnull value, struct RustError* _Nonnull error); +struct InProgressTransactResult in_progress_builder_transact(struct InProgressBuilder*_Nonnull builder); +struct TxReport*_Nullable in_progress_builder_commit(struct InProgressBuilder*_Nonnull builder, struct RustError* _Nonnull error); // 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); +struct EntityBuilder*_Nullable store_entity_builder_from_temp_id(struct Store*_Nonnull store, const char*_Nonnull temp_id, struct RustError* _Nonnull error); +struct EntityBuilder*_Nullable store_entity_builder_from_entid(struct Store*_Nonnull store, const int64_t entid, struct RustError* _Nonnull error) ; +void entity_builder_add_string(struct EntityBuilder*_Nonnull builder, const char*_Nonnull kw, const char*_Nonnull value, struct RustError* _Nonnull error); +void entity_builder_add_long(struct EntityBuilder*_Nonnull builder, const char*_Nonnull kw, const int64_t value, struct RustError* _Nonnull error); +void entity_builder_add_ref(struct EntityBuilder*_Nonnull builder, const char*_Nonnull kw, const int64_t value, struct RustError* _Nonnull error); +void entity_builder_add_keyword(struct EntityBuilder*_Nonnull builder, const char*_Nonnull kw, const char*_Nonnull value, struct RustError* _Nonnull error); +void entity_builder_add_boolean(struct EntityBuilder*_Nonnull builder, const char*_Nonnull kw, const int32_t value, struct RustError* _Nonnull error); +void entity_builder_add_double(struct EntityBuilder*_Nonnull builder, const char*_Nonnull kw, const double value, struct RustError* _Nonnull error); +void entity_builder_add_timestamp(struct EntityBuilder*_Nonnull builder, const char*_Nonnull kw, const int64_t value, struct RustError* _Nonnull error); +void entity_builder_add_uuid(struct EntityBuilder*_Nonnull builder, const char*_Nonnull kw, const uuid_t* _Nonnull value, struct RustError* _Nonnull error); -// Sync -struct Result*_Nonnull store_sync(struct Store*_Nonnull store, const char* _Nonnull user_uuid, const char* _Nonnull server_uri); +void entity_builder_retract_string(struct EntityBuilder*_Nonnull builder, const char*_Nonnull kw, const char*_Nonnull value, struct RustError* _Nonnull error); +void entity_builder_retract_long(struct EntityBuilder*_Nonnull builder, const char*_Nonnull kw, const int64_t value, struct RustError* _Nonnull error); +void entity_builder_retract_ref(struct EntityBuilder*_Nonnull builder, const char*_Nonnull kw, const int64_t value, struct RustError* _Nonnull error); +void entity_builder_retract_keyword(struct EntityBuilder*_Nonnull builder, const char*_Nonnull kw, const char*_Nonnull value, struct RustError* _Nonnull error); +void entity_builder_retract_boolean(struct EntityBuilder*_Nonnull builder, const char*_Nonnull kw, const int32_t value, struct RustError* _Nonnull error); +void entity_builder_retract_double(struct EntityBuilder*_Nonnull builder, const char*_Nonnull kw, const double value, struct RustError* _Nonnull error); +void entity_builder_retract_timestamp(struct EntityBuilder*_Nonnull builder, const char*_Nonnull kw, const int64_t value, struct RustError* _Nonnull error); +void entity_builder_retract_uuid(struct EntityBuilder*_Nonnull builder, const char*_Nonnull kw, const uuid_t* _Nonnull value, struct RustError* _Nonnull error); + +struct InProgressTransactResult entity_builder_transact(struct EntityBuilder*_Nonnull builder); +struct TxReport*_Nullable entity_builder_commit(struct EntityBuilder*_Nonnull builder, struct RustError* _Nonnull error); // Observers void store_register_observer(struct Store*_Nonnull store, const char* _Nonnull key, const int64_t* _Nonnull attributes, const int64_t len, void (*_Nonnull callback_fn)(const char* _Nonnull key, const struct TxChangeList* _Nonnull reports)); @@ -188,7 +194,7 @@ int64_t changelist_entry_at(const struct TxChange* _Nonnull report, size_t index // Query struct Query*_Nonnull store_query(struct Store*_Nonnull store, const char* _Nonnull query); -struct Result*_Nonnull store_value_for_attribute(struct Store*_Nonnull store, const int64_t entid, const char* _Nonnull attribute); +struct TypedValue*_Nullable store_value_for_attribute(struct Store*_Nonnull store, const int64_t entid, const char* _Nonnull attribute, struct RustError* _Nonnull error); // Query Variable Binding void query_builder_bind_long(struct Query*_Nonnull query, const char* _Nonnull var, const int64_t value); @@ -202,20 +208,20 @@ void query_builder_bind_string(struct Query*_Nonnull query, const char* _Nonnull void query_builder_bind_uuid(struct Query*_Nonnull query, const char* _Nonnull var, const uuid_t* _Nonnull value); // Query execution -struct Result*_Nonnull query_builder_execute(struct Query*_Nonnull query); -struct Result*_Nonnull query_builder_execute_scalar(struct Query*_Nonnull query); -struct Result*_Nonnull query_builder_execute_coll(struct Query*_Nonnull query); -struct Result*_Nonnull query_builder_execute_tuple(struct Query*_Nonnull query); +struct QueryResultRows* _Nullable query_builder_execute(struct Query*_Nonnull query, struct RustError* _Nonnull error); +struct TypedValue* _Nullable query_builder_execute_scalar(struct Query*_Nonnull query, struct RustError* _Nonnull error); +struct QueryResultRow* _Nullable query_builder_execute_coll(struct Query*_Nonnull query, struct RustError* _Nonnull error); +struct QueryResultRow* _Nullable query_builder_execute_tuple(struct Query*_Nonnull query, struct RustError* _Nonnull error); // Query Result Processing int64_t typed_value_into_long(struct TypedValue*_Nonnull value); int64_t typed_value_into_entid(struct TypedValue*_Nonnull value); -const char* _Nonnull typed_value_into_kw(struct TypedValue*_Nonnull value); +char* _Nonnull typed_value_into_kw(struct TypedValue*_Nonnull value); int32_t typed_value_into_boolean(struct TypedValue*_Nonnull value); double typed_value_into_double(struct TypedValue*_Nonnull value); int64_t typed_value_into_timestamp(struct TypedValue*_Nonnull value); -const char* _Nonnull typed_value_into_string(struct TypedValue*_Nonnull value); -const uuid_t* _Nonnull typed_value_into_uuid(struct TypedValue*_Nonnull value); +char* _Nonnull typed_value_into_string(struct TypedValue*_Nonnull value); +uuid_t* _Nonnull typed_value_into_uuid(struct TypedValue*_Nonnull value); enum ValueType typed_value_value_type(struct TypedValue*_Nonnull value); struct QueryResultRow* _Nullable row_at_index(struct QueryResultRows* _Nonnull rows, const int32_t index); @@ -227,14 +233,15 @@ struct TypedValue* _Nullable typed_value_list_iter_next(struct QueryRowIterator* struct TypedValue* _Nonnull value_at_index(struct QueryResultRow* _Nonnull row, const int32_t index); int64_t value_at_index_into_long(struct QueryResultRow* _Nonnull row, const int32_t index); int64_t value_at_index_into_entid(struct QueryResultRow* _Nonnull row, const int32_t index); -const char* _Nonnull value_at_index_into_kw(struct QueryResultRow* _Nonnull row, const int32_t index); +char* _Nonnull value_at_index_into_kw(struct QueryResultRow* _Nonnull row, const int32_t index); int32_t value_at_index_into_boolean(struct QueryResultRow* _Nonnull row, const int32_t index); double value_at_index_into_double(struct QueryResultRow* _Nonnull row, const int32_t index); int64_t value_at_index_into_timestamp(struct QueryResultRow* _Nonnull row, const int32_t index); -const char* _Nonnull value_at_index_into_string(struct QueryResultRow* _Nonnull row, const int32_t index); -const uuid_t* _Nonnull value_at_index_into_uuid(struct QueryResultRow* _Nonnull row, const int32_t index); +char* _Nonnull value_at_index_into_string(struct QueryResultRow* _Nonnull row, const int32_t index); +uuid_t* _Nonnull value_at_index_into_uuid(struct QueryResultRow* _Nonnull row, const int32_t index); // Transaction change lists -const struct TxChange* _Nullable tx_change_list_entry_at(const struct TxChangeList* _Nonnull list, size_t index); +const struct TxChange* _Nonnull tx_change_list_entry_at(const struct TxChangeList* _Nonnull list, size_t index); #endif /* store_h */ +