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>"]
|
||||
|
||||
[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 = "../"
|
||||
|
||||
|
|
|
@ -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>);
|
||||
|
|
|
@ -54,6 +54,7 @@ pub use mentat_query::{
|
|||
pub use mentat_db::{
|
||||
CORE_SCHEMA_VERSION,
|
||||
DB_SCHEMA_CORE,
|
||||
AttributeSet,
|
||||
TxObserver,
|
||||
TxReport,
|
||||
new_connection,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue