Update FFI API for use with Swift Library.
Fixing issues raised by tests added to swift library. Improving the API
This commit is contained in:
parent
5fbf17f4f9
commit
10e3d52902
4 changed files with 119 additions and 32 deletions
|
@ -4,11 +4,12 @@ version = "0.0.1"
|
||||||
authors = ["Emily Toop <etoop@mozilla.com>"]
|
authors = ["Emily Toop <etoop@mozilla.com>"]
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "mentat"
|
name = "mentat_ffi"
|
||||||
crate-type = ["staticlib", "cdylib"]
|
crate-type = ["lib", "staticlib", "cdylib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libc = "0.2"
|
libc = "0.2.32"
|
||||||
|
|
||||||
[dependencies.mentat]
|
[dependencies.mentat]
|
||||||
path = ".."
|
path = "../"
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@ pub use mentat::{
|
||||||
Syncable,
|
Syncable,
|
||||||
TypedValue,
|
TypedValue,
|
||||||
TxObserver,
|
TxObserver,
|
||||||
|
TxReport,
|
||||||
Uuid,
|
Uuid,
|
||||||
ValueType,
|
ValueType,
|
||||||
Variable,
|
Variable,
|
||||||
|
@ -62,16 +63,23 @@ pub type TypedValueListIterator = vec::IntoIter<Vec<TypedValue>>;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ExternTxReport {
|
pub struct ExternTempId {
|
||||||
|
pub key: *const c_char,
|
||||||
|
pub value: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TransactionChange {
|
||||||
pub txid: Entid,
|
pub txid: Entid,
|
||||||
pub changes: Box<[Entid]>,
|
|
||||||
pub changes_len: usize,
|
pub changes_len: usize,
|
||||||
|
pub changes: Box<[Entid]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ExternTxReportList {
|
pub struct TxChangeList {
|
||||||
pub reports: Box<[ExternTxReport]>,
|
pub reports: Box<[TransactionChange]>,
|
||||||
pub len: usize,
|
pub len: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,8 +149,8 @@ pub extern "C" fn store_open(uri: *const c_char) -> *mut Store {
|
||||||
pub unsafe extern "C" fn store_transact(store: *mut Store, transaction: *const c_char) -> *mut ExternResult {
|
pub unsafe extern "C" fn store_transact(store: *mut Store, transaction: *const c_char) -> *mut ExternResult {
|
||||||
let store = &mut*store;
|
let store = &mut*store;
|
||||||
let transaction = c_char_to_string(transaction);
|
let transaction = c_char_to_string(transaction);
|
||||||
let result = store.begin_transaction().map(|mut in_progress| {
|
let result = store.begin_transaction().and_then(|mut in_progress| {
|
||||||
in_progress.transact(&transaction).map(|tx_report| {
|
in_progress.transact(&transaction).and_then(|tx_report| {
|
||||||
in_progress.commit()
|
in_progress.commit()
|
||||||
.map(|_| tx_report)
|
.map(|_| tx_report)
|
||||||
})
|
})
|
||||||
|
@ -150,6 +158,29 @@ pub unsafe extern "C" fn store_transact(store: *mut Store, transaction: *const c
|
||||||
Box::into_raw(Box::new(result.into()))
|
Box::into_raw(Box::new(result.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn tx_report_get_entid(tx_report: *mut TxReport) -> i64 {
|
||||||
|
let tx_report = &*tx_report;
|
||||||
|
tx_report.tx_id
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn tx_report_get_tx_instant(tx_report: *mut TxReport) -> i64 {
|
||||||
|
let tx_report = &*tx_report;
|
||||||
|
tx_report.tx_instant.timestamp()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn tx_report_entity_for_temp_id(tx_report: *mut TxReport, tempid: *const c_char) -> *mut i64 {
|
||||||
|
let tx_report = &*tx_report;
|
||||||
|
let key = c_char_to_string(tempid);
|
||||||
|
if let Some(entid) = tx_report.tempids.get(&key) {
|
||||||
|
Box::into_raw(Box::new(entid.clone()))
|
||||||
|
} else {
|
||||||
|
std::ptr::null_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: cache
|
// TODO: cache
|
||||||
|
|
||||||
// TODO: q_once
|
// TODO: q_once
|
||||||
|
@ -161,14 +192,6 @@ pub unsafe extern "C" fn store_query<'a>(store: *mut Store, query: *const c_char
|
||||||
Box::into_raw(Box::new(query_builder))
|
Box::into_raw(Box::new(query_builder))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn query_builder_bind_int(query_builder: *mut QueryBuilder, var: *const c_char, value: c_int) {
|
|
||||||
let var = c_char_to_string(var);
|
|
||||||
let query_builder = &mut*query_builder;
|
|
||||||
let value = value as i32;
|
|
||||||
query_builder.bind_value(&var, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn query_builder_bind_long(query_builder: *mut QueryBuilder, var: *const c_char, value: i64) {
|
pub unsafe extern "C" fn query_builder_bind_long(query_builder: *mut QueryBuilder, var: *const c_char, value: i64) {
|
||||||
let var = c_char_to_string(var);
|
let var = c_char_to_string(var);
|
||||||
|
@ -294,9 +317,9 @@ pub unsafe extern "C" fn typed_value_as_kw(typed_value: *mut TypedValue) -> *co
|
||||||
|
|
||||||
//as_boolean
|
//as_boolean
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn typed_value_as_boolean(typed_value: *mut TypedValue) -> bool {
|
pub unsafe extern "C" fn typed_value_as_boolean(typed_value: *mut TypedValue) -> i32 {
|
||||||
let typed_value = Box::from_raw(typed_value);
|
let typed_value = Box::from_raw(typed_value);
|
||||||
typed_value.into_boolean().expect("Typed value cannot be coerced into a Boolean")
|
if typed_value.into_boolean().expect("Typed value cannot be coerced into a Boolean") { 1 } else { 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
//as_double
|
//as_double
|
||||||
|
@ -310,7 +333,8 @@ pub unsafe extern "C" fn typed_value_as_double(typed_value: *mut TypedValue) ->
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn typed_value_as_timestamp(typed_value: *mut TypedValue) -> i64 {
|
pub unsafe extern "C" fn typed_value_as_timestamp(typed_value: *mut TypedValue) -> i64 {
|
||||||
let typed_value = Box::from_raw(typed_value);
|
let typed_value = Box::from_raw(typed_value);
|
||||||
let val = typed_value.into_timestamp().expect("Typed value cannot be coerced into a Timestamp");
|
let t = typed_value.value_type();
|
||||||
|
let val = typed_value.into_timestamp().expect(&format!("Typed value of type {:?} cannot be coerced into a Timestamp", t));
|
||||||
val
|
val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,6 +352,13 @@ pub unsafe extern "C" fn typed_value_as_uuid(typed_value: *mut TypedValue) -> *
|
||||||
string_to_c_char(typed_value.into_uuid_string().expect("Typed value cannot be coerced into a Uuid"))
|
string_to_c_char(typed_value.into_uuid_string().expect("Typed value cannot be coerced into a Uuid"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//value_type
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn typed_value_value_type(typed_value: *mut TypedValue) -> ValueType {
|
||||||
|
let typed_value = &*typed_value;
|
||||||
|
typed_value.value_type()
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn row_at_index(rows: *mut Vec<Vec<TypedValue>>, index: c_int) -> *mut Vec<TypedValue> {
|
pub unsafe extern "C" fn row_at_index(rows: *mut Vec<Vec<TypedValue>>, index: c_int) -> *mut Vec<TypedValue> {
|
||||||
let result = &*rows;
|
let result = &*rows;
|
||||||
|
@ -353,9 +384,9 @@ pub unsafe extern "C" fn values_iter(values: *mut Vec<TypedValue>) -> *mut Type
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn values_iter_next(iter: *mut TypedValueIterator) -> *const TypedValue {
|
pub unsafe extern "C" fn values_iter_next(iter: *mut TypedValueIterator) -> *mut TypedValue {
|
||||||
let iter = &mut *iter;
|
let iter = &mut *iter;
|
||||||
iter.next().map_or(std::ptr::null_mut(), |v| &v as *const TypedValue)
|
iter.next().map_or(std::ptr::null_mut(), |v| Box::into_raw(Box::new(v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
//as_long
|
//as_long
|
||||||
|
@ -396,7 +427,7 @@ pub unsafe extern "C" fn values_iter_next_as_double(iter: *mut TypedValueIterato
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn values_iter_next_as_timestamp(iter: *mut TypedValueIterator) -> *const i64 {
|
pub unsafe extern "C" fn values_iter_next_as_timestamp(iter: *mut TypedValueIterator) -> *const i64 {
|
||||||
let iter = &mut *iter;
|
let iter = &mut *iter;
|
||||||
iter.next().map_or(std::ptr::null_mut(), |v| v.into_timestamp().expect("Typed value cannot be coerced into a Timestamp") as *const i64)
|
iter.next().map_or(std::ptr::null_mut(), |v| { let t = v.value_type(); v.into_timestamp().expect(&format!("Typed value of type {:?} cannot be coerced into a Timestamp", t)) as *const i64 })
|
||||||
}
|
}
|
||||||
|
|
||||||
//as_string
|
//as_string
|
||||||
|
@ -499,24 +530,24 @@ pub unsafe extern "C" fn store_register_observer(store: *mut Store,
|
||||||
key: *const c_char,
|
key: *const c_char,
|
||||||
attributes: *const Entid,
|
attributes: *const Entid,
|
||||||
attributes_len: usize,
|
attributes_len: usize,
|
||||||
callback: extern fn(key: *const c_char, reports: &ExternTxReportList)) {
|
callback: extern fn(key: *const c_char, reports: &TxChangeList)) {
|
||||||
let store = &mut*store;
|
let store = &mut*store;
|
||||||
let mut attribute_set = BTreeSet::new();
|
let mut attribute_set = BTreeSet::new();
|
||||||
let slice = slice::from_raw_parts(attributes, attributes_len);
|
let slice = slice::from_raw_parts(attributes, attributes_len);
|
||||||
attribute_set.extend(slice.iter());
|
attribute_set.extend(slice.iter());
|
||||||
let key = c_char_to_string(key);
|
let key = c_char_to_string(key);
|
||||||
let tx_observer = Arc::new(TxObserver::new(attribute_set, move |obs_key, batch| {
|
let tx_observer = Arc::new(TxObserver::new(attribute_set, move |obs_key, batch| {
|
||||||
let extern_reports: Vec<ExternTxReport> = batch.into_iter().map(|(tx_id, changes)| {
|
let extern_reports: Vec<TransactionChange> = batch.into_iter().map(|(tx_id, changes)| {
|
||||||
let changes: Vec<Entid> = changes.into_iter().map(|i|*i).collect();
|
let changes: Vec<Entid> = changes.into_iter().map(|i|*i).collect();
|
||||||
let len = changes.len();
|
let len = changes.len();
|
||||||
ExternTxReport {
|
TransactionChange {
|
||||||
txid: *tx_id,
|
txid: *tx_id,
|
||||||
changes: changes.into_boxed_slice(),
|
changes: changes.into_boxed_slice(),
|
||||||
changes_len: len,
|
changes_len: len,
|
||||||
}
|
}
|
||||||
}).collect();
|
}).collect();
|
||||||
let len = extern_reports.len();
|
let len = extern_reports.len();
|
||||||
let reports = ExternTxReportList {
|
let reports = TxChangeList {
|
||||||
reports: extern_reports.into_boxed_slice(),
|
reports: extern_reports.into_boxed_slice(),
|
||||||
len: len,
|
len: len,
|
||||||
};
|
};
|
||||||
|
@ -543,7 +574,7 @@ pub unsafe extern "C" fn store_entid_for_attribute(store: *mut Store, attr: *con
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn tx_report_list_entry_at(tx_report_list: *mut ExternTxReportList, index: c_int) -> *const ExternTxReport {
|
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;
|
let tx_report_list = &*tx_report_list;
|
||||||
let index = index as usize;
|
let index = index as usize;
|
||||||
let report = Box::new(tx_report_list.reports[index].clone());
|
let report = Box::new(tx_report_list.reports[index].clone());
|
||||||
|
@ -551,7 +582,7 @@ pub unsafe extern "C" fn tx_report_list_entry_at(tx_report_list: *mut ExternTxRe
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn changelist_entry_at(tx_report: *mut ExternTxReport, index: c_int) -> Entid {
|
pub unsafe extern "C" fn changelist_entry_at(tx_report: *mut TransactionChange, index: c_int) -> Entid {
|
||||||
let tx_report = &*tx_report;
|
let tx_report = &*tx_report;
|
||||||
let index = index as usize;
|
let index = index as usize;
|
||||||
tx_report.changes[index].clone()
|
tx_report.changes[index].clone()
|
||||||
|
@ -640,8 +671,7 @@ pub unsafe extern "C" fn store_set_uuid_for_attribute_on_entid(store: *mut Store
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn destroy(obj: *mut c_void) {
|
pub unsafe extern "C" fn destroy(obj: *mut c_void) {
|
||||||
if !obj.is_null() {
|
if !obj.is_null() {
|
||||||
let obj_to_release = Box::from_raw(obj);
|
let _ = Box::from_raw(obj);
|
||||||
println!("object to release {:?}", obj_to_release);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -657,6 +687,8 @@ define_destructor!(query_builder_destroy, QueryBuilder);
|
||||||
|
|
||||||
define_destructor!(store_destroy, Store);
|
define_destructor!(store_destroy, Store);
|
||||||
|
|
||||||
|
define_destructor!(tx_report_destroy, TxReport);
|
||||||
|
|
||||||
define_destructor!(typed_value_destroy, TypedValue);
|
define_destructor!(typed_value_destroy, TypedValue);
|
||||||
|
|
||||||
define_destructor!(typed_value_list_destroy, Vec<TypedValue>);
|
define_destructor!(typed_value_list_destroy, Vec<TypedValue>);
|
||||||
|
|
|
@ -54,6 +54,7 @@ pub use mentat_query::{
|
||||||
pub use mentat_db::{
|
pub use mentat_db::{
|
||||||
CORE_SCHEMA_VERSION,
|
CORE_SCHEMA_VERSION,
|
||||||
DB_SCHEMA_CORE,
|
DB_SCHEMA_CORE,
|
||||||
|
AttributeSet,
|
||||||
TxObserver,
|
TxObserver,
|
||||||
TxReport,
|
TxReport,
|
||||||
new_connection,
|
new_connection,
|
||||||
|
|
|
@ -14,9 +14,11 @@ use std::collections::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use mentat_core::{
|
use mentat_core::{
|
||||||
|
DateTime,
|
||||||
Entid,
|
Entid,
|
||||||
NamespacedKeyword,
|
NamespacedKeyword,
|
||||||
TypedValue,
|
TypedValue,
|
||||||
|
Utc,
|
||||||
ValueType,
|
ValueType,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -72,6 +74,11 @@ impl<'a> QueryBuilder<'a> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn bind_date_time(&mut self, var: &str, value: DateTime<Utc>) -> &mut Self {
|
||||||
|
self.values.insert(Variable::from_valid_name(var), TypedValue::Instant(value));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn bind_type(&mut self, var: &str, value_type: ValueType) -> &mut Self {
|
pub fn bind_type(&mut self, var: &str, value_type: ValueType) -> &mut Self {
|
||||||
self.types.insert(Variable::from_valid_name(var), value_type);
|
self.types.insert(Variable::from_valid_name(var), value_type);
|
||||||
self
|
self
|
||||||
|
@ -114,6 +121,11 @@ mod test {
|
||||||
Store,
|
Store,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use mentat_core::{
|
||||||
|
DateTime,
|
||||||
|
Utc,
|
||||||
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_scalar_query() {
|
fn test_scalar_query() {
|
||||||
let mut store = Store::open("").expect("store connection");
|
let mut store = Store::open("").expect("store connection");
|
||||||
|
@ -386,4 +398,45 @@ mod test {
|
||||||
assert_eq!(results.get(0).map_or(None, |t| t.to_owned().into_boolean()).expect("boolean"), true);
|
assert_eq!(results.get(0).map_or(None, |t| t.to_owned().into_boolean()).expect("boolean"), true);
|
||||||
assert_eq!(results.get(1).map_or(None, |t| t.to_owned().into_long()).expect("long"), 25);
|
assert_eq!(results.get(1).map_or(None, |t| t.to_owned().into_long()).expect("long"), 25);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bind_date() {
|
||||||
|
let mut store = Store::open("").expect("store connection");
|
||||||
|
store.transact(r#"[
|
||||||
|
[:db/add "s" :db/ident :foo/boolean]
|
||||||
|
[:db/add "s" :db/valueType :db.type/boolean]
|
||||||
|
[:db/add "s" :db/cardinality :db.cardinality/one]
|
||||||
|
[:db/add "t" :db/ident :foo/long]
|
||||||
|
[:db/add "t" :db/valueType :db.type/long]
|
||||||
|
[:db/add "t" :db/cardinality :db.cardinality/one]
|
||||||
|
[:db/add "i" :db/ident :foo/instant]
|
||||||
|
[:db/add "i" :db/valueType :db.type/instant]
|
||||||
|
[:db/add "i" :db/cardinality :db.cardinality/one]
|
||||||
|
]"#).expect("successful transaction");
|
||||||
|
|
||||||
|
let report = store.transact(r#"[
|
||||||
|
[:db/add "l" :foo/boolean true]
|
||||||
|
[:db/add "l" :foo/long 25]
|
||||||
|
[:db/add "m" :foo/boolean false]
|
||||||
|
[:db/add "m" :foo/long 26]
|
||||||
|
[:db/add "n" :foo/boolean true]
|
||||||
|
[:db/add "n" :foo/long 27]
|
||||||
|
[:db/add "p" :foo/boolean false]
|
||||||
|
[:db/add "p" :foo/long 28]
|
||||||
|
[:db/add "p" :foo/instant #inst "2018-04-11T15:08:00.000Z"]
|
||||||
|
]"#).expect("successful transaction");
|
||||||
|
|
||||||
|
let p_entid = report.tempids.get("p").expect("found it").clone();
|
||||||
|
|
||||||
|
let time = DateTime::parse_from_rfc3339("2018-04-11T19:17:00.000Z")
|
||||||
|
.map(|t| t.with_timezone(&Utc))
|
||||||
|
.expect("expected valid date");
|
||||||
|
let results = QueryBuilder::new(&mut store, r#"[:find [?e ?d] :in ?now :where [?e :foo/instant ?d] [(< ?d ?now)]]"#)
|
||||||
|
.bind_date_time("?now", time)
|
||||||
|
.execute_tuple().expect("TupleResult")
|
||||||
|
.unwrap_or(vec![]);
|
||||||
|
assert_eq!(results.get(0).map_or(None, |t| t.to_owned().into_entid()).expect("entid"), p_entid);
|
||||||
|
let instant = results.get(1).map_or(None, |t| t.to_owned().into_timestamp()).expect("instant");
|
||||||
|
assert_eq!(instant, 1523459280);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue