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:
Emily Toop 2018-04-12 11:21:42 +01:00
parent 5fbf17f4f9
commit 10e3d52902
4 changed files with 119 additions and 32 deletions

View file

@ -4,11 +4,12 @@ version = "0.0.1"
authors = ["Emily Toop <etoop@mozilla.com>"]
[lib]
name = "mentat"
crate-type = ["staticlib", "cdylib"]
name = "mentat_ffi"
crate-type = ["lib", "staticlib", "cdylib"]
[dependencies]
libc = "0.2"
libc = "0.2.32"
[dependencies.mentat]
path = ".."
path = "../"

View file

@ -42,6 +42,7 @@ pub use mentat::{
Syncable,
TypedValue,
TxObserver,
TxReport,
Uuid,
ValueType,
Variable,
@ -62,16 +63,23 @@ pub type TypedValueListIterator = vec::IntoIter<Vec<TypedValue>>;
#[repr(C)]
#[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 changes: Box<[Entid]>,
pub changes_len: usize,
pub changes: Box<[Entid]>,
}
#[repr(C)]
#[derive(Debug)]
pub struct ExternTxReportList {
pub reports: Box<[ExternTxReport]>,
pub struct TxChangeList {
pub reports: Box<[TransactionChange]>,
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 {
let store = &mut*store;
let transaction = c_char_to_string(transaction);
let result = store.begin_transaction().map(|mut in_progress| {
in_progress.transact(&transaction).map(|tx_report| {
let result = store.begin_transaction().and_then(|mut in_progress| {
in_progress.transact(&transaction).and_then(|tx_report| {
in_progress.commit()
.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()))
}
#[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: 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))
}
#[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]
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);
@ -294,9 +317,9 @@ pub unsafe extern "C" fn typed_value_as_kw(typed_value: *mut TypedValue) -> *co
//as_boolean
#[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);
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
@ -310,7 +333,8 @@ pub unsafe extern "C" fn typed_value_as_double(typed_value: *mut TypedValue) ->
#[no_mangle]
pub unsafe extern "C" fn typed_value_as_timestamp(typed_value: *mut TypedValue) -> i64 {
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
}
@ -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"))
}
//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]
pub unsafe extern "C" fn row_at_index(rows: *mut Vec<Vec<TypedValue>>, index: c_int) -> *mut Vec<TypedValue> {
let result = &*rows;
@ -353,9 +384,9 @@ pub unsafe extern "C" fn values_iter(values: *mut Vec<TypedValue>) -> *mut Type
}
#[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;
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
@ -396,7 +427,7 @@ pub unsafe extern "C" fn values_iter_next_as_double(iter: *mut TypedValueIterato
#[no_mangle]
pub unsafe extern "C" fn values_iter_next_as_timestamp(iter: *mut TypedValueIterator) -> *const i64 {
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
@ -499,24 +530,24 @@ pub unsafe extern "C" fn store_register_observer(store: *mut Store,
key: *const c_char,
attributes: *const Entid,
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 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<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 len = changes.len();
ExternTxReport {
TransactionChange {
txid: *tx_id,
changes: changes.into_boxed_slice(),
changes_len: len,
}
}).collect();
let len = extern_reports.len();
let reports = ExternTxReportList {
let reports = TxChangeList {
reports: extern_reports.into_boxed_slice(),
len: len,
};
@ -543,7 +574,7 @@ pub unsafe extern "C" fn store_entid_for_attribute(store: *mut Store, attr: *con
}
#[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 index = index as usize;
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]
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 index = index as usize;
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]
pub unsafe extern "C" fn destroy(obj: *mut c_void) {
if !obj.is_null() {
let obj_to_release = Box::from_raw(obj);
println!("object to release {:?}", obj_to_release);
let _ = Box::from_raw(obj);
}
}
@ -657,6 +687,8 @@ define_destructor!(query_builder_destroy, QueryBuilder);
define_destructor!(store_destroy, Store);
define_destructor!(tx_report_destroy, TxReport);
define_destructor!(typed_value_destroy, TypedValue);
define_destructor!(typed_value_list_destroy, Vec<TypedValue>);

View file

@ -54,6 +54,7 @@ pub use mentat_query::{
pub use mentat_db::{
CORE_SCHEMA_VERSION,
DB_SCHEMA_CORE,
AttributeSet,
TxObserver,
TxReport,
new_connection,

View file

@ -14,9 +14,11 @@ use std::collections::{
};
use mentat_core::{
DateTime,
Entid,
NamespacedKeyword,
TypedValue,
Utc,
ValueType,
};
@ -72,6 +74,11 @@ impl<'a> QueryBuilder<'a> {
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 {
self.types.insert(Variable::from_valid_name(var), value_type);
self
@ -114,6 +121,11 @@ mod test {
Store,
};
use mentat_core::{
DateTime,
Utc,
};
#[test]
fn test_scalar_query() {
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(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);
}
}