Add simple transact string to FFI.

Rename ExternTxReport and ExternTxReportList to distinguish between TxReport from transact and the results of a transaction observer firing.

Move to use c_longlong instead of i64 and time_t as Android and iOS provide different sizes.

Use to_string instead of description for errors as they are more descriptive.

Pass UUID values as bytes rather than strings

Remove unused FFI functions (functionality has been removed)

Handle optional results better - return null if optional is None

Return boolean as integer value rather than bool as Swift can't handle them.

When returning values from an iterator, make sure we don't release the value at the end of the Rust function leaving a dangling pointer.
This commit is contained in:
Emily Toop 2018-04-23 17:26:36 +01:00
parent 60cb5d2432
commit bda1ec4d0a
6 changed files with 189 additions and 180 deletions

View file

@ -755,6 +755,27 @@ impl Binding {
_ => None, _ => None,
} }
} }
pub fn into_c_string(self) -> Option<*mut c_char> {
match self {
Binding::Scalar(v) => v.into_c_string(),
_ => None,
}
}
pub fn into_kw_c_string(self) -> Option<*mut c_char> {
match self {
Binding::Scalar(v) => v.into_kw_c_string(),
_ => None,
}
}
pub fn into_uuid_c_string(self) -> Option<*mut c_char> {
match self {
Binding::Scalar(v) => v.into_uuid_c_string(),
_ => None,
}
}
} }
#[test] #[test]

View file

@ -1,10 +1,14 @@
[package] [package]
name = "mentat_ffi" name = "mentat_ffi"
version = "0.1.0" version = "0.0.1"
authors = ["Emily Toop <etoop@mozilla.com>"] authors = ["Emily Toop <etoop@mozilla.com>"]
[lib]
name = "mentat_ffi"
crate-type = ["lib", "staticlib", "cdylib"]
[dependencies] [dependencies]
libc = "0.2" libc = "0.2"
[dependencies.mentat] [dependencies.mentat]
path = ".." path = "../"

View file

@ -17,6 +17,7 @@ use std::collections::{
use std::os::raw::{ use std::os::raw::{
c_char, c_char,
c_int, c_int,
c_longlong,
c_void, c_void,
}; };
use std::slice; use std::slice;
@ -25,9 +26,8 @@ use std::sync::{
}; };
use std::vec; use std::vec;
use libc::time_t;
pub use mentat::{ pub use mentat::{
Binding,
Entid, Entid,
FindSpec, FindSpec,
HasSchema, HasSchema,
@ -38,15 +38,24 @@ pub use mentat::{
QueryInputs, QueryInputs,
QueryOutput, QueryOutput,
QueryResults, QueryResults,
RelResult,
Store, Store,
Syncable, Syncable,
TypedValue, TypedValue,
TxObserver, TxObserver,
TxReport,
Uuid, Uuid,
ValueType, ValueType,
Variable, Variable,
}; };
pub use mentat::entity_builder::{
BuildTerms,
EntityBuilder,
InProgressBuilder,
IntoThing,
};
pub mod android; pub mod android;
pub mod utils; pub mod utils;
@ -56,21 +65,23 @@ pub use utils::strings::{
string_to_c_char, string_to_c_char,
}; };
pub type TypedValueIterator = vec::IntoIter<TypedValue>; pub use utils::log;
pub type TypedValueListIterator = vec::IntoIter<Vec<TypedValue>>;
pub type BindingIterator = vec::IntoIter<Binding>;
pub type BindingListIterator = std::slice::Chunks<'static, mentat::Binding>;
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ExternTxReport { 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,
} }
@ -106,7 +117,7 @@ impl<T, E> From<Result<T, E>> for ExternResult where E: std::error::Error {
}, },
Err(e) => { Err(e) => {
ExternResult { ExternResult {
err: string_to_c_char(e.description()), err: string_to_c_char(e.to_string()),
ok: std::ptr::null(), ok: std::ptr::null(),
} }
} }
@ -136,6 +147,42 @@ pub extern "C" fn store_open(uri: *const c_char) -> *mut Store {
// TODO: begin_transaction // TODO: begin_transaction
#[no_mangle]
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().and_then(|mut in_progress| {
in_progress.transact(transaction).and_then(|tx_report| {
in_progress.commit()
.map(|_| tx_report)
})
});
Box::into_raw(Box::new(result.into()))
}
#[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
}
#[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
}
#[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;
let key = c_char_to_string(tempid);
if let Some(entid) = tx_report.tempids.get(&key) {
Box::into_raw(Box::new(entid.clone() as c_longlong))
} else {
std::ptr::null_mut()
}
}
// TODO: cache // TODO: cache
// TODO: q_once // TODO: q_once
@ -148,22 +195,14 @@ pub unsafe extern "C" fn store_query<'a>(store: *mut Store, query: *const c_char
} }
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn query_builder_bind_int(query_builder: *mut QueryBuilder, var: *const c_char, value: c_int) { 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);
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); let var = c_char_to_string(var);
let query_builder = &mut*query_builder; let query_builder = &mut*query_builder;
query_builder.bind_long(&var, value); query_builder.bind_long(&var, value);
} }
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn query_builder_bind_ref(query_builder: *mut QueryBuilder, var: *const c_char, value: i64) { 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); let var = c_char_to_string(var);
let query_builder = &mut*query_builder; let query_builder = &mut*query_builder;
query_builder.bind_ref(&var, value); query_builder.bind_ref(&var, value);
@ -205,10 +244,10 @@ pub unsafe extern "C" fn query_builder_bind_double(query_builder: *mut QueryBuil
// instant // instant
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn query_builder_bind_timestamp(query_builder: *mut QueryBuilder, var: *const c_char, value: time_t) { 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); let var = c_char_to_string(var);
let query_builder = &mut*query_builder; let query_builder = &mut*query_builder;
query_builder.bind_instant(&var, value as i64); query_builder.bind_instant(&var, value);
} }
// string // string
@ -222,9 +261,10 @@ pub unsafe extern "C" fn query_builder_bind_string(query_builder: *mut QueryBuil
// uuid // uuid
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn query_builder_bind_uuid(query_builder: *mut QueryBuilder, var: *const c_char, value: *const c_char) { 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); let var = c_char_to_string(var);
let value = Uuid::parse_str(&c_char_to_string(value)).expect("valid uuid"); let value = &*value;
let value = Uuid::from_bytes(value).expect("valid uuid");
let query_builder = &mut*query_builder; let query_builder = &mut*query_builder;
query_builder.bind_value(&var, value); query_builder.bind_value(&var, value);
} }
@ -233,7 +273,12 @@ pub unsafe extern "C" fn query_builder_bind_uuid(query_builder: *mut QueryBuilde
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) -> *mut ExternResult {
let query_builder = &mut*query_builder; let query_builder = &mut*query_builder;
let results = query_builder.execute_scalar(); let results = query_builder.execute_scalar();
Box::into_raw(Box::new(results.into())) 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))
} }
#[no_mangle] #[no_mangle]
@ -247,7 +292,12 @@ pub unsafe extern "C" fn query_builder_execute_coll(query_builder: *mut QueryBui
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) -> *mut ExternResult {
let query_builder = &mut*query_builder; let query_builder = &mut*query_builder;
let results = query_builder.execute_tuple(); let results = query_builder.execute_tuple();
Box::into_raw(Box::new(results.into())) 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))
} }
#[no_mangle] #[no_mangle]
@ -259,162 +309,171 @@ pub unsafe extern "C" fn query_builder_execute(query_builder: *mut QueryBuilder)
// as_long // as_long
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn typed_value_as_long(typed_value: *mut TypedValue) -> i64 { pub unsafe extern "C" fn typed_value_as_long(typed_value: *mut Binding) -> c_longlong {
let typed_value = Box::from_raw(typed_value); let typed_value = Box::from_raw(typed_value);
typed_value.into_long().expect("Typed value cannot be coerced into a Long") typed_value.into_long().expect("Typed value cannot be coerced into a Long")
} }
// as_entid // as_entid
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn typed_value_as_entid(typed_value: *mut TypedValue) -> Entid { pub unsafe extern "C" fn typed_value_as_entid(typed_value: *mut Binding) -> Entid {
let typed_value = Box::from_raw(typed_value); let typed_value = Box::from_raw(typed_value);
typed_value.into_entid().expect("Typed value cannot be coerced into an Entid") typed_value.into_entid().expect("Typed value cannot be coerced into an Entid")
} }
// kw // kw
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn typed_value_as_kw(typed_value: *mut TypedValue) -> *const c_char { pub unsafe extern "C" fn typed_value_as_kw(typed_value: *mut Binding) -> *const c_char {
let typed_value = Box::from_raw(typed_value); let typed_value = Box::from_raw(typed_value);
typed_value.into_kw_c_string().expect("Typed value cannot be coerced into a Namespaced Keyword") typed_value.into_kw_c_string().expect("Typed value cannot be coerced into a Namespaced Keyword")
} }
//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 Binding) -> 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
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn typed_value_as_double(typed_value: *mut TypedValue) -> f64 { pub unsafe extern "C" fn typed_value_as_double(typed_value: *mut Binding) -> f64 {
let typed_value = Box::from_raw(typed_value); let typed_value = Box::from_raw(typed_value);
typed_value.into_double().expect("Typed value cannot be coerced into a Double") typed_value.into_double().expect("Typed value cannot be coerced into a Double")
} }
//as_timestamp //as_timestamp
#[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 Binding) -> c_longlong {
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();
val typed_value.into_timestamp().expect(&format!("Typed value of type {:?} cannot be coerced into a Timestamp", t))
} }
//as_string //as_string
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn typed_value_as_string(typed_value: *mut TypedValue) -> *const c_char { pub unsafe extern "C" fn typed_value_as_string(typed_value: *mut Binding) -> *const c_char {
let typed_value = Box::from_raw(typed_value); let typed_value = Box::from_raw(typed_value);
typed_value.into_c_string().expect("Typed value cannot be coerced into a String") typed_value.into_c_string().expect("Typed value cannot be coerced into a String")
} }
//as_uuid //as_uuid
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn typed_value_as_uuid(typed_value: *mut TypedValue) -> *const c_char { pub unsafe extern "C" fn typed_value_as_uuid(typed_value: *mut Binding) -> *mut [u8; 16] {
let typed_value = Box::from_raw(typed_value); let typed_value = Box::from_raw(typed_value);
typed_value.into_uuid_c_string().expect("Typed value cannot be coerced into a String") let value = typed_value.into_uuid().expect("Typed value cannot be coerced into a Uuid");
Box::into_raw(Box::new(*value.as_bytes()))
}
//value_type
#[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"))
} }
#[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<Binding>>, index: c_int) -> *mut Vec<Binding> {
let result = &*rows; let result = &*rows;
result.get(index as usize).map_or_else(std::ptr::null_mut, |v| Box::into_raw(Box::new(v.clone()))) result.get(index as usize).map_or_else(std::ptr::null_mut, |v| Box::into_raw(Box::new(v.clone())))
} }
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn rows_iter(rows: *mut Vec<Vec<TypedValue>>) -> *mut TypedValueListIterator { pub unsafe extern "C" fn rows_iter(rows: *mut RelResult<Binding>) -> *mut BindingListIterator {
let result = Box::from_raw(rows); let result = &*rows;
Box::into_raw(Box::new(result.into_iter())) let rows = result.rows();
Box::into_raw(Box::new(rows))
} }
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn rows_iter_next(iter: *mut TypedValueListIterator) -> *mut Vec<TypedValue> { pub unsafe extern "C" fn rows_iter_next(iter: *mut BindingListIterator) -> *mut Vec<Binding> {
let iter = &mut *iter; let iter = &mut *iter;
iter.next().map_or(std::ptr::null_mut(), |v| Box::into_raw(Box::new(v))) iter.next().map_or(std::ptr::null_mut(), |v| Box::into_raw(Box::new(v.to_vec())))
} }
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn values_iter(values: *mut Vec<TypedValue>) -> *mut TypedValueIterator { pub unsafe extern "C" fn values_iter(values: *mut Vec<Binding>) -> *mut BindingIterator {
let result = Box::from_raw(values); let result = Box::from_raw(values);
Box::into_raw(Box::new(result.into_iter())) Box::into_raw(Box::new(result.into_iter()))
} }
#[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 BindingIterator) -> *mut Binding {
let iter = &mut *iter; let iter = &mut *iter;
iter.next().map_or_else(std::ptr::null, |v| &v as *const TypedValue) iter.next().map_or(std::ptr::null_mut(), |v| Box::into_raw(Box::new(v)))
} }
//as_long //as_long
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn values_iter_next_as_long(iter: *mut TypedValueIterator) -> *const i64 { pub unsafe extern "C" fn values_iter_next_as_long(iter: *mut BindingIterator) -> *const c_longlong {
let iter = &mut *iter; let iter = &mut *iter;
iter.next().map_or_else(std::ptr::null, |v| &v.into_long().expect("Typed value cannot be coerced into a Long") as *const i64) iter.next().map_or(std::ptr::null_mut(), |v| &v.into_long().expect("Typed value cannot be coerced into a Long") as *const c_longlong)
} }
// as ref // as ref
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn values_iter_next_as_entid(iter: *mut TypedValueIterator) -> *const Entid { pub unsafe extern "C" fn values_iter_next_as_entid(iter: *mut BindingIterator) -> *const Entid {
let iter = &mut *iter; let iter = &mut *iter;
iter.next().map_or_else(std::ptr::null, |v| &v.into_entid().expect("Typed value cannot be coerced into am Entid") as *const Entid) iter.next().map_or_else(std::ptr::null, |v| &v.into_entid().expect("Typed value cannot be coerced into am Entid") as *const Entid)
} }
// as kw // as kw
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn values_iter_next_as_kw(iter: *mut TypedValueIterator) -> *const c_char { pub unsafe extern "C" fn values_iter_next_as_kw(iter: *mut BindingIterator) -> *const c_char {
let iter = &mut *iter; let iter = &mut *iter;
iter.next().map_or_else(std::ptr::null, |v| v.into_kw_c_string().expect("Typed value cannot be coerced into a Namespaced Keyword")) iter.next().map_or_else(std::ptr::null, |v| v.into_kw_c_string().expect("Typed value cannot be coerced into a Namespaced Keyword"))
} }
//as_boolean //as_boolean
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn values_iter_next_as_boolean(iter: *mut TypedValueIterator) -> *const bool { pub unsafe extern "C" fn values_iter_next_as_boolean(iter: *mut BindingIterator) -> *const i32 {
let iter = &mut *iter; let iter = &mut *iter;
iter.next().map_or_else(std::ptr::null, |v| &v.into_boolean().expect("Typed value cannot be coerced into a Boolean") as *const bool) iter.next().map_or(std::ptr::null_mut(), |v| if v.into_boolean().expect("Typed value cannot be coerced into a Boolean") { 1 } else { 0 } as *const i32)
} }
//as_double //as_double
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn values_iter_next_as_double(iter: *mut TypedValueIterator) -> *const f64 { pub unsafe extern "C" fn values_iter_next_as_double(iter: *mut BindingIterator) -> *const f64 {
let iter = &mut *iter; let iter = &mut *iter;
iter.next().map_or_else(std::ptr::null, |v| &v.into_double().expect("Typed value cannot be coerced into a Double") as *const f64) iter.next().map_or_else(std::ptr::null, |v| &v.into_double().expect("Typed value cannot be coerced into a Double") as *const f64)
} }
//as_timestamp //as_timestamp
#[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 BindingIterator) -> *const i64 {
let iter = &mut *iter; let iter = &mut *iter;
iter.next().map_or_else(std::ptr::null, |v| v.into_timestamp().expect("Typed value cannot be coerced into a Timestamp") as *const i64) iter.next().map_or_else(std::ptr::null, |v| v.into_timestamp().expect("Typed value cannot be coerced into a Timestamp") as *const i64)
} }
//as_string //as_string
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn values_iter_next_as_string(iter: *mut TypedValueIterator) -> *const c_char { pub unsafe extern "C" fn values_iter_next_as_string(iter: *mut BindingIterator) -> *const c_char {
let iter = &mut *iter; let iter = &mut *iter;
iter.next().map_or_else(std::ptr::null, |v| v.into_c_string().expect("Typed value cannot be coerced into a String")) iter.next().map_or_else(std::ptr::null, |v| v.into_c_string().expect("Typed value cannot be coerced into a String"))
} }
//as_uuid //as_uuid
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn values_iter_next_as_uuid(iter: *mut TypedValueIterator) -> *const c_char { pub unsafe extern "C" fn values_iter_next_as_uuid(iter: *mut BindingIterator) -> *const c_char {
let iter = &mut *iter; let iter = &mut *iter;
iter.next().map_or_else(std::ptr::null, |v| v.into_uuid_c_string().expect("Typed value cannot be coerced into a Uuid")) iter.next().map_or_else(std::ptr::null, |v| v.into_uuid_c_string().expect("Typed value cannot be coerced into a Uuid"))
} }
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn value_at_index(values: *mut Vec<TypedValue>, index: c_int) -> *const TypedValue { pub unsafe extern "C" fn value_at_index(values: *mut Vec<Binding>, index: c_int) -> *const Binding {
let result = &*values; let result = &*values;
result.get(index as usize).expect("No value at index") as *const TypedValue result.get(index as usize).expect("No value at index") as *const Binding
} }
//as_long //as_long
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn value_at_index_as_long(values: *mut Vec<TypedValue>, index: c_int) -> i64 { pub unsafe extern "C" fn value_at_index_as_long(values: *mut Vec<Binding>, index: c_int) -> c_longlong {
let result = &*values; let result = &*values;
let value = result.get(index as usize).expect("No value at index"); let value = result.get(index as usize).expect("No value at index");
value.clone().into_long().expect("Typed value cannot be coerced into a Long") value.clone().into_long().expect("Typed value cannot be coerced into a Long")
} }
// as ref // as ref
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn value_at_index_as_entid(values: *mut Vec<TypedValue>, index: c_int) -> Entid { pub unsafe extern "C" fn value_at_index_as_entid(values: *mut Vec<Binding>, index: c_int) -> Entid {
let result = &*values; let result = &*values;
let value = result.get(index as usize).expect("No value at index"); let value = result.get(index as usize).expect("No value at index");
value.clone().into_entid().expect("Typed value cannot be coerced into an Entid") value.clone().into_entid().expect("Typed value cannot be coerced into an Entid")
@ -422,7 +481,7 @@ pub unsafe extern "C" fn value_at_index_as_entid(values: *mut Vec<TypedValue>, i
// as kw // as kw
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn value_at_index_as_kw(values: *mut Vec<TypedValue>, index: c_int) -> *const c_char { pub unsafe extern "C" fn value_at_index_as_kw(values: *mut Vec<Binding>, index: c_int) -> *const c_char {
let result = &*values; let result = &*values;
let value = result.get(index as usize).expect("No value at index"); let value = result.get(index as usize).expect("No value at index");
value.clone().into_kw_c_string().expect("Typed value cannot be coerced into a Namespaced Keyword") value.clone().into_kw_c_string().expect("Typed value cannot be coerced into a Namespaced Keyword")
@ -430,15 +489,15 @@ pub unsafe extern "C" fn value_at_index_as_kw(values: *mut Vec<TypedValue>, inde
//as_boolean //as_boolean
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn value_at_index_as_boolean(values: *mut Vec<TypedValue>, index: c_int) -> bool { pub unsafe extern "C" fn value_at_index_as_boolean(values: *mut Vec<Binding>, index: c_int) -> i32 {
let result = &*values; let result = &*values;
let value = result.get(index as usize).expect("No value at index"); let value = result.get(index as usize).expect("No value at index");
value.clone().into_boolean().expect("Typed value cannot be coerced into a Boolean") if value.clone().into_boolean().expect("Typed value cannot be coerced into a Boolean") { 1 } else { 0 }
} }
//as_double //as_double
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn value_at_index_as_double(values: *mut Vec<TypedValue>, index: c_int) -> f64 { pub unsafe extern "C" fn value_at_index_as_double(values: *mut Vec<Binding>, index: c_int) -> f64 {
let result = &*values; let result = &*values;
let value = result.get(index as usize).expect("No value at index"); let value = result.get(index as usize).expect("No value at index");
value.clone().into_double().expect("Typed value cannot be coerced into a Double") value.clone().into_double().expect("Typed value cannot be coerced into a Double")
@ -446,7 +505,7 @@ pub unsafe extern "C" fn value_at_index_as_double(values: *mut Vec<TypedValue>,
//as_timestamp //as_timestamp
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn value_at_index_as_timestamp(values: *mut Vec<TypedValue>, index: c_int) -> i64 { pub unsafe extern "C" fn value_at_index_as_timestamp(values: *mut Vec<Binding>, index: c_int) -> c_longlong {
let result = &*values; let result = &*values;
let value = result.get(index as usize).expect("No value at index"); let value = result.get(index as usize).expect("No value at index");
value.clone().into_timestamp().expect("Typed value cannot be coerced into a timestamp") value.clone().into_timestamp().expect("Typed value cannot be coerced into a timestamp")
@ -454,7 +513,7 @@ pub unsafe extern "C" fn value_at_index_as_timestamp(values: *mut Vec<TypedValue
//as_string //as_string
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn value_at_index_as_string(values: *mut Vec<TypedValue>, index: c_int) -> *mut c_char { pub unsafe extern "C" fn value_at_index_as_string(values: *mut Vec<Binding>, index: c_int) -> *mut c_char {
let result = &*values; let result = &*values;
let value = result.get(index as usize).expect("No value at index"); let value = result.get(index as usize).expect("No value at index");
value.clone().into_c_string().expect("Typed value cannot be coerced into a String") value.clone().into_c_string().expect("Typed value cannot be coerced into a String")
@ -462,26 +521,24 @@ pub unsafe extern "C" fn value_at_index_as_string(values: *mut Vec<TypedValue>,
//as_uuid //as_uuid
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn value_at_index_as_uuid(values: *mut Vec<TypedValue>, index: c_int) -> *mut c_char { pub unsafe extern "C" fn value_at_index_as_uuid(values: *mut Vec<Binding>, index: c_int) -> *mut [u8; 16] {
let result = &*values; let result = &*values;
let value = result.get(index as usize).expect("No value at index"); let value = result.get(index as usize).expect("No value at index");
value.clone().into_uuid_c_string().expect("Typed value cannot be coerced into a Uuid") let uuid = value.clone().into_uuid().expect("Typed value cannot be coerced into a Uuid");
Box::into_raw(Box::new(*uuid.as_bytes()))
} }
// TODO: q_prepare
// TODO: q_explain
// TODO: lookup_values_for_attribute
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn store_value_for_attribute(store: *mut Store, entid: i64, 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) -> *mut ExternResult {
let store = &*store; let store = &*store;
let kw = kw_from_string(c_char_to_string(attribute)); let kw = kw_from_string(c_char_to_string(attribute));
let value = match store.lookup_value_for_attribute(entid, &kw) { let value = match store.lookup_value_for_attribute(entid, &kw) {
Ok(Some(v)) => ExternResult { ok: Box::into_raw(Box::new(v)) as *const _ as *const c_void, err: std::ptr::null() }, 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() }, Ok(None) => ExternResult { ok: std::ptr::null(), err: std::ptr::null() },
Err(e) => ExternResult { ok: std::ptr::null(), err: string_to_c_char(e.description()) }, Err(e) => ExternResult { ok: std::ptr::null(), err: string_to_c_char(e.to_string()) },
}; };
Box::into_raw(Box::new(value)) Box::into_raw(Box::new(value))
} }
@ -491,24 +548,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,
}; };
@ -535,7 +592,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());
@ -543,7 +600,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()
@ -558,82 +615,10 @@ pub unsafe extern "C" fn store_sync(store: *mut Store, user_uuid: *const c_char,
Box::into_raw(Box::new(res.into())) Box::into_raw(Box::new(res.into()))
} }
fn assert_datom<E, V>(store: &mut Store, entid: E, attribute: String, value: V) -> *mut ExternResult
where E: Into<KnownEntid>,
V: Into<TypedValue> {
let kw = kw_from_string(attribute);
let res = store.assert_datom(entid.into(), kw, value.into());
Box::into_raw(Box::new(res.into()))
}
#[no_mangle]
pub unsafe extern "C" fn store_set_long_for_attribute_on_entid(store: *mut Store, entid: Entid, attribute: *const c_char, value: i64) -> *mut ExternResult {
let store = &mut*store;
let kw = kw_from_string(c_char_to_string(attribute));
let res = store.assert_datom(KnownEntid(entid), kw, TypedValue::Long(value));
Box::into_raw(Box::new(res.into()))
}
#[no_mangle]
pub unsafe extern "C" fn store_set_entid_for_attribute_on_entid(store: *mut Store, entid: Entid, attribute: *const c_char, value: Entid) -> *mut ExternResult {
let store = &mut*store;
let kw = kw_from_string(c_char_to_string(attribute));
let res = store.assert_datom(KnownEntid(entid), kw, TypedValue::Ref(value));
Box::into_raw(Box::new(res.into()))
}
#[no_mangle]
pub unsafe extern "C" fn store_set_kw_ref_for_attribute_on_entid(store: *mut Store, entid: Entid, attribute: *const c_char, value: *const c_char) -> *mut ExternResult {
let store = &mut*store;
let kw = kw_from_string(c_char_to_string(attribute));
let value = kw_from_string(c_char_to_string(value));
let is_valid = store.conn().current_schema().get_entid(&value);
if is_valid.is_none() {
return Box::into_raw(Box::new(ExternResult { ok: std::ptr::null_mut(), err: string_to_c_char(format!("Unknown attribute {:?}", value)) }));
}
let kw_entid = is_valid.unwrap();
let res = store.assert_datom(KnownEntid(entid), kw, TypedValue::Ref(kw_entid.into()));
Box::into_raw(Box::new(res.into()))
}
#[no_mangle]
pub unsafe extern "C" fn store_set_boolean_for_attribute_on_entid(store: *mut Store, entid: Entid, attribute: *const c_char, value: bool) -> *mut ExternResult {
let store = &mut*store;
assert_datom(store, KnownEntid(entid), c_char_to_string(attribute), value)
}
#[no_mangle]
pub unsafe extern "C" fn store_set_double_for_attribute_on_entid(store: *mut Store, entid: Entid, attribute: *const c_char, value: f64) -> *mut ExternResult {
let store = &mut*store;
assert_datom(store, KnownEntid(entid), c_char_to_string(attribute), value)
}
#[no_mangle]
pub unsafe extern "C" fn store_set_timestamp_for_attribute_on_entid(store: *mut Store, entid: Entid, attribute: *const c_char, value: time_t) -> *mut ExternResult {
let store = &mut*store;
let kw = kw_from_string(c_char_to_string(attribute));
let res = store.assert_datom(KnownEntid(entid), kw, TypedValue::instant(value as i64));
Box::into_raw(Box::new(res.into()))
}
#[no_mangle]
pub unsafe extern "C" fn store_set_string_for_attribute_on_entid(store: *mut Store, entid: Entid, attribute: *const c_char, value: *const c_char) -> *mut ExternResult {
let store = &mut*store;
assert_datom(store, KnownEntid(entid), c_char_to_string(attribute), c_char_to_string(value))
}
#[no_mangle]
pub unsafe extern "C" fn store_set_uuid_for_attribute_on_entid(store: *mut Store, entid: Entid, attribute: *const c_char, value: *const c_char) -> *mut ExternResult {
let store = &mut*store;
let uuid = Uuid::parse_str(&c_char_to_string(value)).expect("valid uuid");
assert_datom(store, KnownEntid(entid), c_char_to_string(attribute), uuid)
}
#[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);
} }
} }
@ -649,12 +634,14 @@ define_destructor!(query_builder_destroy, QueryBuilder);
define_destructor!(store_destroy, Store); define_destructor!(store_destroy, Store);
define_destructor!(typed_value_destroy, TypedValue); define_destructor!(tx_report_destroy, TxReport);
define_destructor!(typed_value_list_destroy, Vec<TypedValue>); define_destructor!(typed_value_destroy, Binding);
define_destructor!(typed_value_list_iter_destroy, TypedValueIterator); define_destructor!(typed_value_list_destroy, Vec<Binding>);
define_destructor!(typed_value_result_set_destroy, Vec<Vec<TypedValue>>); define_destructor!(typed_value_list_iter_destroy, BindingIterator);
define_destructor!(typed_value_result_set_iter_destroy, TypedValueListIterator); define_destructor!(typed_value_result_set_destroy, RelResult<Binding>);
define_destructor!(typed_value_result_set_iter_destroy, BindingListIterator);

View file

@ -94,7 +94,6 @@ use mentat_tolstoy::Syncer;
use uuid::Uuid; use uuid::Uuid;
use entity_builder::{ use entity_builder::{
BuildTerms,
InProgressBuilder, InProgressBuilder,
TermBuilder, TermBuilder,
}; };
@ -647,10 +646,6 @@ impl Store {
pub fn unregister_observer(&mut self, key: &String) { pub fn unregister_observer(&mut self, key: &String) {
self.conn.unregister_observer(key); self.conn.unregister_observer(key);
} }
pub fn assert_datom<T>(&mut self, entid: T, attribute: Keyword, value: TypedValue) -> Result<()> where T: Into<KnownEntid> {
self.conn.assert_datom(&mut self.sqlite, entid, attribute, value)
}
} }
impl Queryable for Store { impl Queryable for Store {
@ -973,18 +968,6 @@ impl Conn {
pub fn unregister_observer(&mut self, key: &String) { pub fn unregister_observer(&mut self, key: &String) {
self.tx_observer_service.lock().unwrap().deregister(key); self.tx_observer_service.lock().unwrap().deregister(key);
} }
// TODO: expose the entity builder over FFI and remove the need for this function entirely
// It's really only here in order to keep the FFI layer as thin as possible.
// Once the entity builder is exposed, we can perform all of these functions over FFI from the client.
pub fn assert_datom<T>(&mut self, sqlite: &mut rusqlite::Connection, entid: T, attribute: Keyword, value: TypedValue) -> Result<()> where T: Into<KnownEntid> {
let in_progress = self.begin_transaction(sqlite)?;
let mut builder = in_progress.builder().describe(entid.into());
builder.add_kw(&attribute, value)?;
builder.commit()
.map_err(|e| e.into())
.and(Ok(()))
}
} }
#[cfg(test)] #[cfg(test)]

View file

@ -55,6 +55,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,

View file

@ -14,10 +14,12 @@ use std::collections::{
}; };
use mentat_core::{ use mentat_core::{
DateTime,
Entid, Entid,
Keyword, Keyword,
Binding, Binding,
TypedValue, TypedValue,
Utc,
ValueType, ValueType,
}; };
@ -71,6 +73,12 @@ impl<'a> QueryBuilder<'a> {
pub fn bind_instant(&mut self, var: &str, value: i64) -> &mut Self { pub fn bind_instant(&mut self, var: &str, value: i64) -> &mut Self {
self.values.insert(Variable::from_valid_name(var), TypedValue::instant(value)); self.values.insert(Variable::from_valid_name(var), TypedValue::instant(value));
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 self
} }
@ -116,6 +124,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");