diff --git a/ffi/src/lib.rs b/ffi/src/lib.rs index e26e79ef..72194912 100644 --- a/ffi/src/lib.rs +++ b/ffi/src/lib.rs @@ -8,6 +8,66 @@ // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. +//! This module exposes an Foreign Function Interface (FFI) that allows Mentat to be +//! called from other languages. +//! +//! Functions that are available to other languages in this module are defined as +//! extern "C" functions which allow them to be layed out correctly for the +//! platform's C ABI. They all have a `#[no_mangle]` decorator to ensure +//! Rust's name mangling is turned off, so that it is easier to link to. +//! +//! Mentat's FFI contains unsafe code. As it is an interface between foreign code +//! and native Rust code, Rust cannot guarentee that the types and data that it is +//! handling that have been passed to it from another language are present and in +//! the format it is expecting. +//! This interface is designed to ensure that nothing unsafe ever leaves this module +//! and enters Mentat proper. +//! +//! Structs defined with `#[repr(C)]` are guaranteed to have a layout that is compatible +//! with the platform's representation in C. +//! +//! This API passes pointers in two ways, depending on the lifetime of the object and +//! what object owns it. +//! Pointers to values that can guarenteed to live beyond the lifetime of the function, +//! are passed over the FFI as a raw pointer. +//! ``` +//! value as *const TypedValue +//! ``` +//! Pointers to values that cannot be guarenteed to live beyond the lifetime of the function +//! are first Boxed so that they live on the heap, and the raw pointer passed this way. +//! ``` +//! Box::into_raw(Box::new(value)) +//! ``` +//! +//! The memory for values that are moved onto the heap before being passed over the FFI +//! are no longer managed by Rust, but Rust still owns the value. Therefore the pointer +//! must be returned to Rust in order to be released. To this effect a number of `destructor` +//! function are provided for each Rust value type that is passed, and a catch all destructor +//! to destroy memory for `#[repr(C)]` values. The destructors reclaim the memory via `Box` +//! and then drop the reference, causing the memory to be released. +//! +//! A macro has been provided to make defining destructors easier. +//! ``` +//! define_destructor!(query_builder_destroy, QueryBuilder); +//! ``` +//! +//! Passing a pointer to released memory will cause Mentat to crash, so callers have to be +//! careful to ensure they manage their pointers properly. Failure to call a destructor for a +//! value on the heap will cause a memory leak. +//! +//! Most of the functions exposed in this FFI have a direct mapping to existing Mentat APIs. +//! Application logic has been kept to a minumum in order to provide the greatest flexibility +//! for callers using the interface, however there are a one exception where several steps +//! have been wrapped into a single call in order to make the interface easier to use. +//! `store_register_observer` takes a single native callback function is wrapped inside a +//! Rust closure and added to a `TxObserver` struct that this then used to register the +//! observer with the store. +//! +//! Result and 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` such that conversion from the Rust type +//! to the C type is as painless as possible. +//! extern crate libc; extern crate mentat; @@ -70,6 +130,16 @@ pub use utils::log; pub type BindingIterator = vec::IntoIter; pub type BindingListIterator = std::slice::Chunks<'static, mentat::Binding>; +/// A C representation of the a 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 { @@ -78,6 +148,14 @@ pub struct TransactionChange { pub changes: Box<[Entid]>, } + /// 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. #[repr(C)] #[derive(Debug)] pub struct TxChangeList { @@ -85,6 +163,15 @@ pub struct TxChangeList { pub len: usize, } +/// A C representation Rust's 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 { @@ -99,6 +186,16 @@ impl From> for ExternOption { } } +/// A C representation Rust's 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 { @@ -125,11 +222,17 @@ impl From> for ExternResult where E: std::error::Error { } } -// 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 -// used by the Store in order to avoid a memory leak. -// TODO: Start returning `ExternResult`s rather than crashing on error. +/// 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 +/// used by the Store in order to avoid a memory leak. +/// TODO: Start returning `ExternResult`s rather than crashing on error. +/// +/// # Safety +/// +/// Callers are responsible for managing the memory for the return value. +/// A destructor `store_destroy` is provided for releasing the memory for this +/// pointer type. #[no_mangle] pub extern "C" fn store_open(uri: *const c_char) -> *mut Store { let uri = c_char_to_string(uri); @@ -147,6 +250,18 @@ pub extern "C" fn store_open(uri: *const c_char) -> *mut Store { // TODO: begin_transaction +/// Performs a single transaction against the store. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `Store` is not dangling and that +/// the C string provided to `transaction` is valid. +/// +/// Callers are responsible for managing the memory for the return value. +/// A destructor `destroy` is provided for releasing the memory for this +/// pointer type. +/// +/// TODO: Document the errors that can result from transact #[no_mangle] pub unsafe extern "C" fn store_transact(store: *mut Store, transaction: *const c_char) -> *mut ExternResult { let store = &mut*store; @@ -160,18 +275,35 @@ pub unsafe extern "C" fn store_transact(store: *mut Store, transaction: *const c Box::into_raw(Box::new(result.into())) } +/// Fetches the `tx_id` for the given `TxReport`. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `TxReport` is not dangling. #[no_mangle] pub unsafe extern "C" fn tx_report_get_entid(tx_report: *mut TxReport) -> c_longlong { let tx_report = &*tx_report; tx_report.tx_id as c_longlong } +/// Fetches the `tx_instant` for the given `TxReport`. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `TxReport` is not dangling. #[no_mangle] pub unsafe extern "C" fn tx_report_get_tx_instant(tx_report: *mut TxReport) -> c_longlong { let tx_report = &*tx_report; tx_report.tx_instant.timestamp() as c_longlong } +/// Fetches the `Entid` assigned to the `tempid` during the transaction represented +/// by the given `TxReport`. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `TxReport` is not dangling and that +/// the C string provided to `tempid` is valid. #[no_mangle] pub unsafe extern "C" fn tx_report_entity_for_temp_id(tx_report: *mut TxReport, tempid: *const c_char) -> *mut c_longlong { let tx_report = &*tx_report; @@ -186,6 +318,19 @@ pub unsafe extern "C" fn tx_report_entity_for_temp_id(tx_report: *mut TxReport, // TODO: cache // TODO: q_once + +/// Creates a `QueryBuilder` from the given store to execute the provided query. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `Store` is not dangling and that +/// the C string provided to `query` is valid. +/// +/// Callers are responsible for managing the memory for the return value. +/// A destructor `query_builder_destroy` is provided for releasing the memory for this +/// pointer type. +/// +/// TODO: Update QueryBuilder so it only takes a `Store` pointer on execution #[no_mangle] pub unsafe extern "C" fn store_query<'a>(store: *mut Store, query: *const c_char) -> *mut QueryBuilder<'a> { let query = c_char_to_string(query); @@ -194,6 +339,12 @@ pub unsafe extern "C" fn store_query<'a>(store: *mut Store, query: *const c_char Box::into_raw(Box::new(query_builder)) } +/// Binds a `TypedValue::Long` to a `Variable` with the given name. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `QueryBuilder` is not dangling and that +/// the C string provided to `var` is valid. #[no_mangle] pub unsafe extern "C" fn query_builder_bind_long(query_builder: *mut QueryBuilder, var: *const c_char, value: c_longlong) { let var = c_char_to_string(var); @@ -201,6 +352,12 @@ pub unsafe extern "C" fn query_builder_bind_long(query_builder: *mut QueryBuilde query_builder.bind_long(&var, value); } +/// Binds a `TypedValue::Ref` to a `Variable` with the given name. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `QueryBuilder` is not dangling and that +/// the C string provided to `var` is valid. #[no_mangle] pub unsafe extern "C" fn query_builder_bind_ref(query_builder: *mut QueryBuilder, var: *const c_char, value: c_longlong) { let var = c_char_to_string(var); @@ -208,6 +365,17 @@ pub unsafe extern "C" fn query_builder_bind_ref(query_builder: *mut QueryBuilder query_builder.bind_ref(&var, value); } +/// Binds a `TypedValue::Ref` to a `Variable` with the given name. Takes a keyword as a c string in the format +/// `:namespace/name` and converts it into an `NamespacedKeyword`. +/// +/// # Panics +/// +/// If the provided keyword does not map to a valid keyword in the schema. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `QueryBuilder` is not dangling and that +/// the C strings provided to `var` and `value` are valid. #[no_mangle] pub unsafe extern "C" fn query_builder_bind_ref_kw(query_builder: *mut QueryBuilder, var: *const c_char, value: *const c_char) { let var = c_char_to_string(var); @@ -218,6 +386,13 @@ pub unsafe extern "C" fn query_builder_bind_ref_kw(query_builder: *mut QueryBuil } } +/// Binds a `TypedValue::Keyword` to a `Variable` with the given name. Takes a keyword as a c string in the format +/// `:namespace/name` and converts it into an `NamespacedKeyword`. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `QueryBuilder` is not dangling and that +/// the C strings provided to `var` and `value` are valid. #[no_mangle] pub unsafe extern "C" fn query_builder_bind_kw(query_builder: *mut QueryBuilder, var: *const c_char, value: *const c_char) { let var = c_char_to_string(var); @@ -226,7 +401,12 @@ pub unsafe extern "C" fn query_builder_bind_kw(query_builder: *mut QueryBuilder, query_builder.bind_value(&var, kw); } -// boolean +/// Binds a `TypedValue::Boolean` to a `Variable` with the given name. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `QueryBuilder` is not dangling and that +/// the C string provided to `var` is valid. #[no_mangle] pub unsafe extern "C" fn query_builder_bind_boolean(query_builder: *mut QueryBuilder, var: *const c_char, value: bool) { let var = c_char_to_string(var); @@ -234,7 +414,12 @@ pub unsafe extern "C" fn query_builder_bind_boolean(query_builder: *mut QueryBui query_builder.bind_value(&var, value); } -// double +/// Binds a `TypedValue::Double` to a `Variable` with the given name. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `QueryBuilder` is not dangling and that +/// the C string provided to `var` is valid. #[no_mangle] pub unsafe extern "C" fn query_builder_bind_double(query_builder: *mut QueryBuilder, var: *const c_char, value: f64) { let var = c_char_to_string(var); @@ -242,7 +427,13 @@ pub unsafe extern "C" fn query_builder_bind_double(query_builder: *mut QueryBuil query_builder.bind_value(&var, value); } -// instant +/// Binds a `TypedValue::Instant` to a `Variable` with the given name. +/// Takes a timestamp in microseconds. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `QueryBuilder` is not dangling and that +/// the C string provided to `var` is valid. #[no_mangle] pub unsafe extern "C" fn query_builder_bind_timestamp(query_builder: *mut QueryBuilder, var: *const c_char, value: c_longlong) { let var = c_char_to_string(var); @@ -250,7 +441,12 @@ pub unsafe extern "C" fn query_builder_bind_timestamp(query_builder: *mut QueryB query_builder.bind_instant(&var, value); } -// string +/// Binds a `TypedValue::String` to a `Variable` with the given name. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `QueryBuilder` is not dangling and that +/// the C strings provided to `var` and `value` are valid. #[no_mangle] pub unsafe extern "C" fn query_builder_bind_string(query_builder: *mut QueryBuilder, var: *const c_char, value: *const c_char) { let var = c_char_to_string(var); @@ -259,7 +455,13 @@ pub unsafe extern "C" fn query_builder_bind_string(query_builder: *mut QueryBuil query_builder.bind_value(&var, value); } -// uuid +/// Binds a `TypedValue::Uuid` to a `Variable` with the given name. +/// Takes a UUID as a byte slice of length 16. This maps directly to the `uuid_t` C type. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `QueryBuilder` is not dangling and that +/// the C string provided to `var` is valid. #[no_mangle] pub unsafe extern "C" fn query_builder_bind_uuid(query_builder: *mut QueryBuilder, var: *const c_char, value: *mut [u8; 16]) { let var = c_char_to_string(var); @@ -269,6 +471,19 @@ pub unsafe extern "C" fn query_builder_bind_uuid(query_builder: *mut QueryBuilde query_builder.bind_value(&var, value); } +/// Executes a query and returns the results as a Scalar. +/// +/// # Panics +/// +/// If the query executed is not structured as Scalar. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `QueryBuilder` is not dangling. +/// +/// Callers are responsible for managing the memory for the return value. +/// A destructor `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 { let query_builder = &mut*query_builder; @@ -281,6 +496,19 @@ pub unsafe extern "C" fn query_builder_execute_scalar(query_builder: *mut QueryB Box::into_raw(Box::new(extern_result)) } +/// Executes a query and returns the results as a Coll. +/// +/// # Panics +/// +/// If the query executed is not structured as Coll. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `QueryBuilder` is not dangling. +/// +/// Callers are responsible for managing the memory for the return value. +/// A destructor `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 { let query_builder = &mut*query_builder; @@ -288,6 +516,19 @@ pub unsafe extern "C" fn query_builder_execute_coll(query_builder: *mut QueryBui Box::into_raw(Box::new(results.into())) } +/// Executes a query and returns the results as a Tuple. +/// +/// # Panics +/// +/// If the query executed is not structured as Tuple. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `QueryBuilder` is not dangling. +/// +/// Callers are responsible for managing the memory for the return value. +/// A destructor `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 { let query_builder = &mut*query_builder; @@ -300,6 +541,19 @@ pub unsafe extern "C" fn query_builder_execute_tuple(query_builder: *mut QueryBu Box::into_raw(Box::new(extern_result)) } +/// Executes a query and returns the results as a Rel. +/// +/// # Panics +/// +/// If the query executed is not structured as Rel. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `QueryBuilder` is not dangling. +/// +/// Callers are responsible for managing the memory for the return value. +/// A destructor `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 { let query_builder = &mut*query_builder; @@ -314,56 +568,122 @@ fn unwrap_conversion(value: Option, expected_type: ValueType) -> T { } } -// as_long +/// Consumes a `TypedValue` and returns the value as a `long`. +/// +/// # Panics +/// +/// If the `ValueType` of the `TypedValue` is not `ValueType::Long`. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `TypedValue` is not dangling . #[no_mangle] pub unsafe extern "C" fn typed_value_as_long(typed_value: *mut Binding) -> c_longlong { let typed_value = Box::from_raw(typed_value); unwrap_conversion(typed_value.into_long(), ValueType::Long) } -// as_entid +/// Consumes a `TypedValue` and returns the value as an `Entid`. +/// +/// # Panics +/// +/// If the `ValueType` of the `TypedValue` is not `ValueType::Ref`. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `TypedValue` is not dangling . #[no_mangle] pub unsafe extern "C" fn typed_value_as_entid(typed_value: *mut Binding) -> Entid { let typed_value = Box::from_raw(typed_value); unwrap_conversion(typed_value.into_entid(), ValueType::Ref) } -// kw +/// Consumes a `TypedValue` and returns the value as an keyword `String`. +/// +/// # Panics +/// +/// If the `ValueType` of the `TypedValue` is not `ValueType::Ref`. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `TypedValue` is not dangling . #[no_mangle] pub unsafe extern "C" fn typed_value_as_kw(typed_value: *mut Binding) -> *const c_char { let typed_value = Box::from_raw(typed_value); unwrap_conversion(typed_value.into_kw_c_string(), ValueType::Keyword) as *const c_char } -//as_boolean +/// Consumes a `TypedValue` and returns the value as a boolean represented as an `i32`. +/// If the value of the boolean is `true` the value returned is 1. +/// If the value of the boolean is `false` the value returned is 0. +/// +/// # Panics +/// +/// If the `ValueType` of the `TypedValue` is not `ValueType::Boolean`. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `TypedValue` is not dangling . #[no_mangle] pub unsafe extern "C" fn typed_value_as_boolean(typed_value: *mut Binding) -> i32 { let typed_value = Box::from_raw(typed_value); if unwrap_conversion(typed_value.into_boolean(), ValueType::Boolean) { 1 } else { 0 } } -//as_double +/// Consumes a `TypedValue` and returns the value as a `f64`. +/// +/// # Panics +/// +/// If the `ValueType` of the `TypedValue` is not `ValueType::Double`. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `TypedValue` is not dangling . #[no_mangle] pub unsafe extern "C" fn typed_value_as_double(typed_value: *mut Binding) -> f64 { let typed_value = Box::from_raw(typed_value); unwrap_conversion(typed_value.into_double(), ValueType::Double) } -//as_timestamp +/// Consumes a `TypedValue` and returns the value as a microsecond timestamp. +/// +/// # Panics +/// +/// If the `ValueType` of the `TypedValue` is not `ValueType::Instant`. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `TypedValue` is not dangling . #[no_mangle] pub unsafe extern "C" fn typed_value_as_timestamp(typed_value: *mut Binding) -> c_longlong { let typed_value = Box::from_raw(typed_value); unwrap_conversion(typed_value.into_timestamp(), ValueType::Instant) } -//as_string +/// Consumes a `TypedValue` and returns the value as a `String`. +/// +/// # Panics +/// +/// If the `ValueType` of the `TypedValue` is not `ValueType::String`. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `TypedValue` is not dangling . #[no_mangle] pub unsafe extern "C" fn typed_value_as_string(typed_value: *mut Binding) -> *const c_char { let typed_value = Box::from_raw(typed_value); unwrap_conversion(typed_value.into_c_string(), ValueType::String) as *const c_char } -//as_uuid +/// Consumes a `TypedValue` and returns the value as a UUID byte slice of length 16. +/// +/// # Panics +/// +/// If the `ValueType` of the `TypedValue` is not `ValueType::Uuid`. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `TypedValue` is not dangling . #[no_mangle] pub unsafe extern "C" fn typed_value_as_uuid(typed_value: *mut Binding) -> *mut [u8; 16] { let typed_value = Box::from_raw(typed_value); @@ -371,19 +691,42 @@ pub unsafe extern "C" fn typed_value_as_uuid(typed_value: *mut Binding) -> *mut Box::into_raw(Box::new(*value.as_bytes())) } -//value_type +/// Returns the `ValueType` of this `TypedValue`. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `TypedValue` is not dangling . #[no_mangle] pub unsafe extern "C" fn typed_value_value_type(typed_value: *mut Binding) -> ValueType { let typed_value = &*typed_value; typed_value.value_type().unwrap_or_else(|| panic!("Binding is not Scalar and has no ValueType")) } +/// Returns the value at the provided `index` as a `Vec`. +/// If there is no value present at the `index`, a null pointer is returned. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `Vec>` is not dangling . +/// +/// Callers are responsible for managing the memory for the return value. +/// A destructor `typed_value_result_set_destroy` is provided for releasing the memory for this +/// pointer type. #[no_mangle] pub unsafe extern "C" fn row_at_index(rows: *mut RelResult, index: c_int) -> *mut Vec { let result = &*rows; result.row(index as usize).map_or_else(std::ptr::null_mut, |v| Box::into_raw(Box::new(v.to_vec()))) } +/// Consumes the `Vec>` and returns an iterator over the values. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `Vec>` is not dangling . +/// +/// Callers are responsible for managing the memory for the return value. +/// A destructor `typed_value_result_set_iter_destroy` is provided for releasing the memory for this +/// pointer type. #[no_mangle] pub unsafe extern "C" fn rows_iter(rows: *mut RelResult) -> *mut BindingListIterator { let result = &*rows; @@ -391,38 +734,96 @@ pub unsafe extern "C" fn rows_iter(rows: *mut RelResult) -> *mut Bindin Box::into_raw(Box::new(rows)) } +/// Returns the next value in the `iter` as a `Vec`. +/// If there is no value next value, a null pointer is returned. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `Vec>` is not dangling . +/// +/// Callers are responsible for managing the memory for the return value. +/// A destructor `typed_value_list_destroy` is provided for releasing the memory for this +/// pointer type. #[no_mangle] pub unsafe extern "C" fn rows_iter_next(iter: *mut BindingListIterator) -> *mut Vec { let iter = &mut *iter; iter.next().map_or(std::ptr::null_mut(), |v| Box::into_raw(Box::new(v.to_vec()))) } +/// Consumes the `Vec` and returns an iterator over the values. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `Vec` is not dangling . +/// +/// Callers are responsible for managing the memory for the return value. +/// A destructor `typed_value_list_iter_destroy` is provided for releasing the memory for this +/// pointer type. #[no_mangle] pub unsafe extern "C" fn values_iter(values: *mut Vec) -> *mut BindingIterator { let result = Box::from_raw(values); Box::into_raw(Box::new(result.into_iter())) } +/// Returns the next value in the `iter` as a `ValueType`. +/// If there is no value next value, a null pointer is returned. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `Vec` is not dangling . +/// +/// 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 values_iter_next(iter: *mut BindingIterator) -> *mut Binding { let iter = &mut *iter; iter.next().map_or(std::ptr::null_mut(), |v| Box::into_raw(Box::new(v))) } +/// Returns the value at the provided `index` as a `ValueType`. +/// If there is no value present at the `index`, a null pointer is returned. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `Vec` is not dangling . +/// +/// 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 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 } -//as_long +/// Returns the value of the `TypedValue` at `index` as a `long`. +/// +/// # Panics +/// +/// If the `ValueType` of the `TypedValue` is not `ValueType::Long`. +/// If there is no value at `index`. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `Vec` is not dangling . #[no_mangle] pub unsafe extern "C" fn value_at_index_as_long(values: *mut Vec, index: c_int) -> c_longlong { let result = &*values; let value = result.get(index as usize).expect("No value at index"); unwrap_conversion(value.clone().into_long(), ValueType::Long) } -// as ref + +/// Returns the value of the `TypedValue` at `index` as an `Entid`. +/// +/// # Panics +/// +/// If the `ValueType` of the `TypedValue` is not `ValueType::Ref`. +/// If there is no value at `index`. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `Vec` is not dangling . #[no_mangle] pub unsafe extern "C" fn value_at_index_as_entid(values: *mut Vec, index: c_int) -> Entid { let result = &*values; @@ -430,7 +831,16 @@ pub unsafe extern "C" fn value_at_index_as_entid(values: *mut Vec, inde unwrap_conversion(value.clone().into_entid(), ValueType::Ref) } -// as kw +/// Returns the value of the `TypedValue` at `index` as a keyword `String`. +/// +/// # Panics +/// +/// If the `ValueType` of the `TypedValue` is not `ValueType::Ref`. +/// If there is no value at `index`. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `Vec` is not dangling . #[no_mangle] pub unsafe extern "C" fn value_at_index_as_kw(values: *mut Vec, index: c_int) -> *const c_char { let result = &*values; @@ -438,7 +848,18 @@ pub unsafe extern "C" fn value_at_index_as_kw(values: *mut Vec, index: unwrap_conversion(value.clone().into_kw_c_string(), ValueType::Keyword) as *const c_char } -//as_boolean +/// Returns the value of the `TypedValue` at `index` as a boolean represented by a `i32`. +/// If the value of the `boolean` is `true` then the value returned is 1. +/// If the value of the `boolean` is `false` then the value returned is 0. +/// +/// # Panics +/// +/// If the `ValueType` of the `TypedValue` is not `ValueType::Long`. +/// If there is no value at `index`. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `Vec` is not dangling . #[no_mangle] pub unsafe extern "C" fn value_at_index_as_boolean(values: *mut Vec, index: c_int) -> i32 { let result = &*values; @@ -446,7 +867,16 @@ pub unsafe extern "C" fn value_at_index_as_boolean(values: *mut Vec, in if unwrap_conversion(value.clone().into_boolean(), ValueType::Boolean) { 1 } else { 0 } } -//as_double +/// Returns the value of the `TypedValue` at `index` as an `f64`. +/// +/// # Panics +/// +/// If the `ValueType` of the `TypedValue` is not `ValueType::Double`. +/// If there is no value at `index`. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `Vec` is not dangling . #[no_mangle] pub unsafe extern "C" fn value_at_index_as_double(values: *mut Vec, index: c_int) -> f64 { let result = &*values; @@ -454,7 +884,16 @@ pub unsafe extern "C" fn value_at_index_as_double(values: *mut Vec, ind unwrap_conversion(value.clone().into_double(), ValueType::Double) } -//as_timestamp +/// Returns the value of the `TypedValue` at `index` as a microsecond timestamp. +/// +/// # Panics +/// +/// If the `ValueType` of the `TypedValue` is not `ValueType::instant`. +/// If there is no value at `index`. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `Vec` is not dangling . #[no_mangle] pub unsafe extern "C" fn value_at_index_as_timestamp(values: *mut Vec, index: c_int) -> c_longlong { let result = &*values; @@ -462,7 +901,16 @@ pub unsafe extern "C" fn value_at_index_as_timestamp(values: *mut Vec, unwrap_conversion(value.clone().into_timestamp(), ValueType::Instant) } -//as_string +/// Returns the value of the `TypedValue` at `index` as an `String`. +/// +/// # Panics +/// +/// If the `ValueType` of the `TypedValue` is not `ValueType::String`. +/// If there is no value at `index`. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `Vec` is not dangling . #[no_mangle] pub unsafe extern "C" fn value_at_index_as_string(values: *mut Vec, index: c_int) -> *const c_char { let result = &*values; @@ -470,7 +918,16 @@ pub unsafe extern "C" fn value_at_index_as_string(values: *mut Vec, ind unwrap_conversion(value.clone().into_c_string(), ValueType::String) as *const c_char } -//as_uuid +/// Returns the value of the `TypedValue` at `index` as a UUID byte slice of length 16. +/// +/// # Panics +/// +/// If the `ValueType` of the `TypedValue` is not `ValueType::Uuid`. +/// If there is no value at `index`. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `Vec` is not dangling . #[no_mangle] pub unsafe extern "C" fn value_at_index_as_uuid(values: *mut Vec, index: c_int) -> *mut [u8; 16] { let result = &*values; @@ -479,6 +936,22 @@ pub unsafe extern "C" fn value_at_index_as_uuid(values: *mut Vec, index Box::into_raw(Box::new(*uuid.as_bytes())) } +/// Returns an `ExternResult` containing the `TypedValue` 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`. +/// 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` in the `Schema` for the given `attribute` then an error is returned in `err`. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `Store` is not dangling . +/// +/// Callers are responsible for managing the memory for the return value. +/// A destructor `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 { let store = &*store; @@ -494,6 +967,19 @@ pub unsafe extern "C" fn store_value_for_attribute(store: *mut Store, entid: c_l Box::into_raw(Box::new(value)) } +/// Registers a `TxObserver` with the `key` to observe changes to `attributes` +/// on this `store`. +/// Calls `callback` is a relevant transaction occurs. +/// +/// # Panics +/// +/// If there is no `Attribute` in the `Schema` for a given `attribute`. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `Store` is not dangling, that the +/// C string in `key` is valid and that the function pointer is not dangling. +/// #[no_mangle] pub unsafe extern "C" fn store_register_observer(store: *mut Store, key: *const c_char, @@ -525,6 +1011,12 @@ pub unsafe extern "C" fn store_register_observer(store: *mut Store, store.register_observer(key, tx_observer); } +/// Unregisters a `TxObserver` with the `key` to observe changes on this `store`. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `Store` is not dangling, that the +/// C string in `key` is valid. #[no_mangle] pub unsafe extern "C" fn store_unregister_observer(store: *mut Store, key: *const c_char) { let store = &mut*store; @@ -532,6 +1024,16 @@ pub unsafe extern "C" fn store_unregister_observer(store: *mut Store, key: *cons store.unregister_observer(&key); } +/// Returns the `Entid` associated with the `attr` as `:namespace/name`. +/// +/// # Panics +/// +/// If there is no `Attribute` in the `Schema` for `attr`. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `Store` is not dangling, that the +/// C string in `attr` is valid. #[no_mangle] pub unsafe extern "C" fn store_entid_for_attribute(store: *mut Store, attr: *const c_char) -> Entid { let store = &mut*store; @@ -542,6 +1044,19 @@ pub unsafe extern "C" fn store_entid_for_attribute(store: *mut Store, attr: *con current_schema.get_entid(&kw).expect("Unable to find entid for invalid attribute").into() } +/// Returns the value at the provided `index` as a `TransactionChange`. +/// +/// # Panics +/// +/// If there is no value present at the `index`. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `TxChangeList` is not dangling . +/// +/// 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 { let tx_report_list = &*tx_report_list; @@ -550,6 +1065,15 @@ pub unsafe extern "C" fn tx_change_list_entry_at(tx_report_list: *mut TxChangeLi Box::into_raw(report) } +/// Returns the value at the provided `index` as a `Entid`. +/// +/// # Panics +/// +/// If there is no value present at the `index`. +/// +/// # Safety +/// +/// Callers must ensure that the pointer to the `TransactionChange` is not dangling . #[no_mangle] pub unsafe extern "C" fn changelist_entry_at(tx_report: *mut TransactionChange, index: c_int) -> Entid { let tx_report = &*tx_report; @@ -557,15 +1081,7 @@ pub unsafe extern "C" fn changelist_entry_at(tx_report: *mut TransactionChange, tx_report.changes[index].clone() } -#[no_mangle] -pub unsafe extern "C" fn store_sync(store: *mut Store, user_uuid: *const c_char, server_uri: *const c_char) -> *mut ExternResult { - let store = &mut*store; - let user_uuid = c_char_to_string(user_uuid); - let server_uri = c_char_to_string(server_uri); - let res = store.sync(&server_uri, &user_uuid); - Box::into_raw(Box::new(res.into())) -} - +/// destroy function for releasing the memory for `repr(C)` structs. #[no_mangle] pub unsafe extern "C" fn destroy(obj: *mut c_void) { if !obj.is_null() { @@ -573,6 +1089,7 @@ pub unsafe extern "C" fn destroy(obj: *mut c_void) { } } +/// Creates a function with a given `$name` that releases the memroy for a type `$t`. macro_rules! define_destructor ( ($name:ident, $t:ty) => ( #[no_mangle] @@ -581,10 +1098,14 @@ macro_rules! define_destructor ( } ) ); + +/// Destructor for releasing the memory of `QueryBuilder`. define_destructor!(query_builder_destroy, QueryBuilder); +/// Destructor for releasing the memory of `Store`. define_destructor!(store_destroy, Store); +/// Destructor for releasing the memory of `TxReport`. define_destructor!(tx_report_destroy, TxReport); define_destructor!(typed_value_destroy, Binding); diff --git a/sdks/swift/Mentat/Mentat/store.h b/sdks/swift/Mentat/Mentat/store.h index ff735c2f..26fd80f0 100644 --- a/sdks/swift/Mentat/Mentat/store.h +++ b/sdks/swift/Mentat/Mentat/store.h @@ -13,35 +13,56 @@ #include #include +/* + * This file contains headers for all of the structs and functions that map directly to the functions + * defined in mentat/ffi/src/lib.rs. + * + * The C in this file is specifically formatted to be used with Objective C and Swift and contains + * macros and flags that will not be recognised by other C based languages. + */ + +/* + A mapping of the TxChange repr(C) Rust object. + The memory for this is managed by Swift. + */ struct TxChange { int64_t txid; int64_t*_Nonnull* _Nonnull changes; uint64_t len; }; +/* + A mapping of the TxChangeList repr(C) Rust object. + The memory for this is managed by Swift. + */ struct TxChangeList { struct TxChange*_Nonnull* _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; }; typedef struct Result Result; +/* + A mapping of the ExternOption repr(C) Rust object. + The memory for this is managed by Swift. + */ struct Option { void* _Nullable value; }; typedef struct Option Option; -struct InProgressTransactResult { - struct InProgress*_Nonnull inProgress; - struct Result*_Nonnull result; -}; -typedef struct InProgressTransactResult InProgressTransactResult; - +/* + A Mapping for the ValueType Rust object. + */ typedef NS_ENUM(NSInteger, ValueType) { ValueTypeRef = 1, ValueTypeBoolean, @@ -53,6 +74,7 @@ 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; @@ -68,6 +90,7 @@ struct TypedValue; // Store struct Store*_Nonnull store_open(const char*_Nonnull uri); +// Destructors. void destroy(void* _Nullable obj); void query_builder_destroy(struct Query* _Nullable obj); void store_destroy(struct Store* _Nonnull obj);