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");