From bda1ec4d0a98b9eb716f3edeff32b49a032d4ec8 Mon Sep 17 00:00:00 2001 From: Emily Toop Date: Mon, 23 Apr 2018 17:26:36 +0100 Subject: [PATCH] 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. --- core/src/types.rs | 21 +++ ffi/Cargo.toml | 8 +- ffi/src/lib.rs | 309 +++++++++++++++++++++---------------------- src/conn.rs | 17 --- src/lib.rs | 1 + src/query_builder.rs | 13 ++ 6 files changed, 189 insertions(+), 180 deletions(-) diff --git a/core/src/types.rs b/core/src/types.rs index ad426a12..ca3b86d4 100644 --- a/core/src/types.rs +++ b/core/src/types.rs @@ -755,6 +755,27 @@ impl Binding { _ => 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] diff --git a/ffi/Cargo.toml b/ffi/Cargo.toml index cd872585..7c1e7c10 100644 --- a/ffi/Cargo.toml +++ b/ffi/Cargo.toml @@ -1,10 +1,14 @@ [package] name = "mentat_ffi" -version = "0.1.0" +version = "0.0.1" authors = ["Emily Toop "] +[lib] +name = "mentat_ffi" +crate-type = ["lib", "staticlib", "cdylib"] + [dependencies] libc = "0.2" [dependencies.mentat] -path = ".." +path = "../" diff --git a/ffi/src/lib.rs b/ffi/src/lib.rs index b657e871..50416aea 100644 --- a/ffi/src/lib.rs +++ b/ffi/src/lib.rs @@ -17,6 +17,7 @@ use std::collections::{ use std::os::raw::{ c_char, c_int, + c_longlong, c_void, }; use std::slice; @@ -25,9 +26,8 @@ use std::sync::{ }; use std::vec; -use libc::time_t; - pub use mentat::{ + Binding, Entid, FindSpec, HasSchema, @@ -38,15 +38,24 @@ pub use mentat::{ QueryInputs, QueryOutput, QueryResults, + RelResult, Store, Syncable, TypedValue, TxObserver, + TxReport, Uuid, ValueType, Variable, }; +pub use mentat::entity_builder::{ + BuildTerms, + EntityBuilder, + InProgressBuilder, + IntoThing, +}; + pub mod android; pub mod utils; @@ -56,21 +65,23 @@ pub use utils::strings::{ string_to_c_char, }; -pub type TypedValueIterator = vec::IntoIter; -pub type TypedValueListIterator = vec::IntoIter>; +pub use utils::log; + +pub type BindingIterator = vec::IntoIter; +pub type BindingListIterator = std::slice::Chunks<'static, mentat::Binding>; #[repr(C)] #[derive(Debug, Clone)] -pub struct ExternTxReport { +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, } @@ -106,7 +117,7 @@ impl From> for ExternResult where E: std::error::Error { }, Err(e) => { ExternResult { - err: string_to_c_char(e.description()), + err: string_to_c_char(e.to_string()), ok: std::ptr::null(), } } @@ -136,6 +147,42 @@ pub extern "C" fn store_open(uri: *const c_char) -> *mut Store { // 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: q_once @@ -148,22 +195,14 @@ pub unsafe extern "C" fn store_query<'a>(store: *mut Store, query: *const c_char } #[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) { +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; query_builder.bind_long(&var, value); } #[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 query_builder = &mut*query_builder; query_builder.bind_ref(&var, value); @@ -205,10 +244,10 @@ pub unsafe extern "C" fn query_builder_bind_double(query_builder: *mut QueryBuil // instant #[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 query_builder = &mut*query_builder; - query_builder.bind_instant(&var, value as i64); + query_builder.bind_instant(&var, value); } // string @@ -222,9 +261,10 @@ pub unsafe extern "C" fn query_builder_bind_string(query_builder: *mut QueryBuil // uuid #[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 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; 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 { let query_builder = &mut*query_builder; 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] @@ -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 { let query_builder = &mut*query_builder; 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] @@ -259,162 +309,171 @@ pub unsafe extern "C" fn query_builder_execute(query_builder: *mut QueryBuilder) // as_long #[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); typed_value.into_long().expect("Typed value cannot be coerced into a Long") } // as_entid #[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); typed_value.into_entid().expect("Typed value cannot be coerced into an Entid") } // kw #[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); typed_value.into_kw_c_string().expect("Typed value cannot be coerced into a Namespaced Keyword") } //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 Binding) -> 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 #[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); typed_value.into_double().expect("Typed value cannot be coerced into a Double") } //as_timestamp #[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 val = typed_value.into_timestamp().expect("Typed value cannot be coerced into a Timestamp"); - val + let t = typed_value.value_type(); + typed_value.into_timestamp().expect(&format!("Typed value of type {:?} cannot be coerced into a Timestamp", t)) } //as_string #[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); typed_value.into_c_string().expect("Typed value cannot be coerced into a String") } //as_uuid #[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); - 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] -pub unsafe extern "C" fn row_at_index(rows: *mut Vec>, index: c_int) -> *mut Vec { +pub unsafe extern "C" fn row_at_index(rows: *mut Vec>, index: c_int) -> *mut Vec { let result = &*rows; result.get(index as usize).map_or_else(std::ptr::null_mut, |v| Box::into_raw(Box::new(v.clone()))) } #[no_mangle] -pub unsafe extern "C" fn rows_iter(rows: *mut Vec>) -> *mut TypedValueListIterator { - let result = Box::from_raw(rows); - Box::into_raw(Box::new(result.into_iter())) +pub unsafe extern "C" fn rows_iter(rows: *mut RelResult) -> *mut BindingListIterator { + let result = &*rows; + let rows = result.rows(); + Box::into_raw(Box::new(rows)) } #[no_mangle] -pub unsafe extern "C" fn rows_iter_next(iter: *mut TypedValueListIterator) -> *mut Vec { +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))) + iter.next().map_or(std::ptr::null_mut(), |v| Box::into_raw(Box::new(v.to_vec()))) } #[no_mangle] -pub unsafe extern "C" fn values_iter(values: *mut Vec) -> *mut TypedValueIterator { +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())) } #[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; - 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 #[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; - 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 #[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; 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 #[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; 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 #[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; - 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 #[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; 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 #[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; 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 #[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; iter.next().map_or_else(std::ptr::null, |v| v.into_c_string().expect("Typed value cannot be coerced into a String")) } //as_uuid #[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; 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] -pub unsafe extern "C" fn value_at_index(values: *mut Vec, index: c_int) -> *const TypedValue { +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 TypedValue + result.get(index as usize).expect("No value at index") as *const Binding } //as_long #[no_mangle] -pub unsafe extern "C" fn value_at_index_as_long(values: *mut Vec, index: c_int) -> i64 { +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"); value.clone().into_long().expect("Typed value cannot be coerced into a Long") } // as ref #[no_mangle] -pub unsafe extern "C" fn value_at_index_as_entid(values: *mut Vec, index: c_int) -> Entid { +pub unsafe extern "C" fn value_at_index_as_entid(values: *mut Vec, index: c_int) -> Entid { let result = &*values; 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") @@ -422,7 +481,7 @@ pub unsafe extern "C" fn value_at_index_as_entid(values: *mut Vec, i // as kw #[no_mangle] -pub unsafe extern "C" fn value_at_index_as_kw(values: *mut Vec, index: c_int) -> *const c_char { +pub unsafe extern "C" fn value_at_index_as_kw(values: *mut Vec, index: c_int) -> *const c_char { let result = &*values; 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") @@ -430,15 +489,15 @@ pub unsafe extern "C" fn value_at_index_as_kw(values: *mut Vec, inde //as_boolean #[no_mangle] -pub unsafe extern "C" fn value_at_index_as_boolean(values: *mut Vec, index: c_int) -> bool { +pub unsafe extern "C" fn value_at_index_as_boolean(values: *mut Vec, index: c_int) -> i32 { let result = &*values; 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 #[no_mangle] -pub unsafe extern "C" fn value_at_index_as_double(values: *mut Vec, index: c_int) -> f64 { +pub unsafe extern "C" fn value_at_index_as_double(values: *mut Vec, index: c_int) -> f64 { let result = &*values; 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") @@ -446,7 +505,7 @@ pub unsafe extern "C" fn value_at_index_as_double(values: *mut Vec, //as_timestamp #[no_mangle] -pub unsafe extern "C" fn value_at_index_as_timestamp(values: *mut Vec, index: c_int) -> i64 { +pub unsafe extern "C" fn value_at_index_as_timestamp(values: *mut Vec, index: c_int) -> c_longlong { let result = &*values; 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") @@ -454,7 +513,7 @@ pub unsafe extern "C" fn value_at_index_as_timestamp(values: *mut Vec, index: c_int) -> *mut c_char { +pub unsafe extern "C" fn value_at_index_as_string(values: *mut Vec, index: c_int) -> *mut c_char { let result = &*values; 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") @@ -462,26 +521,24 @@ pub unsafe extern "C" fn value_at_index_as_string(values: *mut Vec, //as_uuid #[no_mangle] -pub unsafe extern "C" fn value_at_index_as_uuid(values: *mut Vec, index: c_int) -> *mut c_char { +pub unsafe extern "C" fn value_at_index_as_uuid(values: *mut Vec, index: c_int) -> *mut [u8; 16] { let result = &*values; 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] -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 kw = kw_from_string(c_char_to_string(attribute)); 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() }, - 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)) } @@ -491,24 +548,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 = batch.into_iter().map(|(tx_id, changes)| { + let extern_reports: Vec = batch.into_iter().map(|(tx_id, changes)| { let changes: Vec = 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, }; @@ -535,7 +592,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()); @@ -543,7 +600,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() @@ -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())) } -fn assert_datom(store: &mut Store, entid: E, attribute: String, value: V) -> *mut ExternResult -where E: Into, - V: Into { - 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] 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); } } @@ -649,12 +634,14 @@ define_destructor!(query_builder_destroy, QueryBuilder); define_destructor!(store_destroy, Store); -define_destructor!(typed_value_destroy, TypedValue); +define_destructor!(tx_report_destroy, TxReport); -define_destructor!(typed_value_list_destroy, Vec); +define_destructor!(typed_value_destroy, Binding); -define_destructor!(typed_value_list_iter_destroy, TypedValueIterator); +define_destructor!(typed_value_list_destroy, Vec); -define_destructor!(typed_value_result_set_destroy, Vec>); +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); + +define_destructor!(typed_value_result_set_iter_destroy, BindingListIterator); diff --git a/src/conn.rs b/src/conn.rs index 813afe74..585c9943 100644 --- a/src/conn.rs +++ b/src/conn.rs @@ -94,7 +94,6 @@ use mentat_tolstoy::Syncer; use uuid::Uuid; use entity_builder::{ - BuildTerms, InProgressBuilder, TermBuilder, }; @@ -647,10 +646,6 @@ impl Store { pub fn unregister_observer(&mut self, key: &String) { self.conn.unregister_observer(key); } - - pub fn assert_datom(&mut self, entid: T, attribute: Keyword, value: TypedValue) -> Result<()> where T: Into { - self.conn.assert_datom(&mut self.sqlite, entid, attribute, value) - } } impl Queryable for Store { @@ -973,18 +968,6 @@ impl Conn { pub fn unregister_observer(&mut self, key: &String) { 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(&mut self, sqlite: &mut rusqlite::Connection, entid: T, attribute: Keyword, value: TypedValue) -> Result<()> where T: Into { - 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)] diff --git a/src/lib.rs b/src/lib.rs index 029ad1e9..d73b9529 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -55,6 +55,7 @@ pub use mentat_query::{ pub use mentat_db::{ CORE_SCHEMA_VERSION, DB_SCHEMA_CORE, + AttributeSet, TxObserver, TxReport, new_connection, diff --git a/src/query_builder.rs b/src/query_builder.rs index 2d998eb7..984bab2c 100644 --- a/src/query_builder.rs +++ b/src/query_builder.rs @@ -14,10 +14,12 @@ use std::collections::{ }; use mentat_core::{ + DateTime, Entid, Keyword, Binding, TypedValue, + Utc, ValueType, }; @@ -71,6 +73,12 @@ impl<'a> QueryBuilder<'a> { pub fn bind_instant(&mut self, var: &str, value: i64) -> &mut Self { self.values.insert(Variable::from_valid_name(var), TypedValue::instant(value)); + + self + } + + pub fn bind_date_time(&mut self, var: &str, value: DateTime) -> &mut Self { + self.values.insert(Variable::from_valid_name(var), TypedValue::Instant(value)); self } @@ -116,6 +124,11 @@ mod test { Store, }; + use mentat_core::{ + DateTime, + Utc, + }; + #[test] fn test_scalar_query() { let mut store = Store::open("").expect("store connection");