use crate::errors::AppError;
use crate::ffi::errors::{Error, Result};
use crate::ffi::helper::send_sync;
use crate::ffi::object_cache::MDataEntriesHandle;
use crate::App;
use ffi_utils::callback::Callback;
use ffi_utils::try_cb;
use ffi_utils::{
catch_unwind_cb, vec_clone_from_raw_parts, FfiResult, OpaqueCtx, SafePtr, FFI_RESULT_OK,
};
use safe_core::core_structs::{
MDataEntry as NativeMDataEntry, MDataKey as NativeMDataKey, MDataValue as NativeMDataValue,
};
use safe_core::ffi::ipc::resp::MDataEntry;
use safe_core::CoreError;
use safe_nd::{Error as NdError, MDataSeqValue};
use std::collections::BTreeMap;
use std::os::raw::c_void;
#[no_mangle]
pub unsafe extern "C" fn seq_mdata_entries_new(
app: *const App,
user_data: *mut c_void,
o_cb: extern "C" fn(
user_data: *mut c_void,
result: *const FfiResult,
entries_h: MDataEntriesHandle,
),
) {
catch_unwind_cb(user_data, o_cb, || {
send_sync(app, user_data, o_cb, |_, context| {
Ok(context
.object_cache()
.insert_seq_mdata_entries(BTreeMap::<Vec<u8>, MDataSeqValue>::default()))
})
})
}
#[no_mangle]
pub unsafe extern "C" fn seq_mdata_entries_insert(
app: *const App,
entries_h: MDataEntriesHandle,
key: *const u8,
key_len: usize,
value: *const u8,
value_len: usize,
user_data: *mut c_void,
o_cb: extern "C" fn(user_data: *mut c_void, result: *const FfiResult),
) {
catch_unwind_cb(user_data, o_cb, || {
let key = vec_clone_from_raw_parts(key, key_len);
let value = vec_clone_from_raw_parts(value, value_len);
with_entries(app, entries_h, user_data, o_cb, |entries| {
let _ = entries.insert(
key,
MDataSeqValue {
data: value,
version: 0,
},
);
Ok(())
})
})
}
#[no_mangle]
pub unsafe extern "C" fn seq_mdata_entries_len(
app: *const App,
entries_h: MDataEntriesHandle,
user_data: *mut c_void,
o_cb: extern "C" fn(user_data: *mut c_void, result: *const FfiResult, len: usize),
) {
catch_unwind_cb(user_data, o_cb, || {
with_entries(app, entries_h, user_data, o_cb, |entries| Ok(entries.len()))
})
}
#[no_mangle]
pub unsafe extern "C" fn seq_mdata_entries_get(
app: *const App,
entries_h: MDataEntriesHandle,
key: *const u8,
key_len: usize,
user_data: *mut c_void,
o_cb: extern "C" fn(
user_data: *mut c_void,
result: *const FfiResult,
content: *const u8,
content_len: usize,
version: u64,
),
) {
catch_unwind_cb(user_data, o_cb, || {
let user_data = OpaqueCtx(user_data);
let key = vec_clone_from_raw_parts(key, key_len);
(*app).send(move |_, context| {
let entries = try_cb!(
context
.object_cache()
.get_seq_mdata_entries(entries_h)
.map_err(Error::from),
user_data,
o_cb
);
let value = entries
.get(&key)
.ok_or(NdError::NoSuchEntry)
.map_err(CoreError::from)
.map_err(AppError::from);
let value = try_cb!(value.map_err(Error::from), user_data, o_cb);
o_cb(
user_data.0,
FFI_RESULT_OK,
value.data.as_safe_ptr(),
value.data.len(),
value.version,
);
None
})
})
}
#[no_mangle]
pub unsafe extern "C" fn seq_mdata_list_entries(
app: *const App,
entries_h: MDataEntriesHandle,
user_data: *mut c_void,
o_cb: extern "C" fn(
user_data: *mut c_void,
result: *const FfiResult,
entries: *const MDataEntry,
entries_len: usize,
),
) {
let user_data = OpaqueCtx(user_data);
catch_unwind_cb(user_data, o_cb, || {
(*app).send(move |_client, context| {
let entries = try_cb!(
context
.object_cache()
.get_seq_mdata_entries(entries_h)
.map_err(Error::from),
user_data.0,
o_cb
);
let entries_vec: Vec<MDataEntry> = entries
.iter()
.map(|(key, value)| {
NativeMDataEntry {
key: NativeMDataKey(key.clone()),
value: NativeMDataValue::from_routing(value.clone()),
}
.into_repr_c()
})
.collect();
o_cb(
user_data.0,
FFI_RESULT_OK,
entries_vec.as_safe_ptr(),
entries_vec.len(),
);
None
})
})
}
#[no_mangle]
pub unsafe extern "C" fn seq_mdata_entries_free(
app: *const App,
entries_h: MDataEntriesHandle,
user_data: *mut c_void,
o_cb: extern "C" fn(user_data: *mut c_void, result: *const FfiResult),
) {
catch_unwind_cb(user_data, o_cb, || {
send_sync(app, user_data, o_cb, move |_, context| {
let _ = context.object_cache().remove_seq_mdata_entries(entries_h)?;
Ok(())
})
})
}
unsafe fn with_entries<C, F>(
app: *const App,
entries_h: MDataEntriesHandle,
user_data: *mut c_void,
o_cb: C,
f: F,
) -> Result<()>
where
C: Callback + Copy + Send + 'static,
F: FnOnce(&mut BTreeMap<Vec<u8>, MDataSeqValue>) -> Result<C::Args> + Send + 'static,
{
send_sync(app, user_data, o_cb, move |_, context| {
let mut entries = context.object_cache().get_seq_mdata_entries(entries_h)?;
f(&mut *entries)
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ffi::mdata_info::*;
use crate::ffi::mutable_data::entry_actions::*;
use crate::ffi::mutable_data::permissions::*;
use crate::ffi::mutable_data::*;
use crate::ffi::object_cache::MDataEntryActionsHandle;
use crate::run;
use crate::test_utils::create_app;
use ffi_utils::test_utils::{
call_0, call_1, call_vec, send_via_user_data, sender_as_user_data,
};
use ffi_utils::vec_clone_from_raw_parts;
use safe_core::btree_map;
use safe_core::core_structs::{MDataEntry, MDataKey, MDataValue};
use safe_core::utils;
use safe_nd::{MDataAction, MDataPermissionSet, MDataSeqValue};
use std::os::raw::c_void;
use std::sync::mpsc;
#[test]
fn entries() {
let app = create_app();
let key0 = b"key0".to_vec();
let key1 = b"key1".to_vec();
let value0 = MDataSeqValue {
data: unwrap!(utils::generate_random_vector(10)),
version: 0,
};
let value1 = MDataSeqValue {
data: unwrap!(utils::generate_random_vector(10)),
version: 2,
};
let entries = btree_map![key0.clone() => value0.clone(),
key1.clone() => value1.clone()];
let handle0 = unwrap!(run(&app, move |_, context| {
Ok(context.object_cache().insert_seq_mdata_entries(entries))
}));
let len1: usize = unsafe {
unwrap!(call_1(|ud, cb| seq_mdata_entries_len(
&app, handle0, ud, cb
)))
};
let handle1 = unsafe {
let handle = unwrap!(call_1(|ud, cb| seq_mdata_entries_new(&app, ud, cb)));
unwrap!(call_0(|ud, cb| seq_mdata_entries_insert(
&app,
handle,
key0.as_ptr(),
key0.len(),
value0.data.as_ptr(),
value0.data.len(),
ud,
cb,
)));
unwrap!(call_0(|ud, cb| seq_mdata_entries_insert(
&app,
handle,
key1.as_ptr(),
key1.len(),
value1.data.as_ptr(),
value1.data.len(),
ud,
cb,
)));
handle
};
let len2: usize = unsafe {
unwrap!(call_1(|ud, cb| seq_mdata_entries_len(
&app, handle1, ud, cb
)))
};
assert_eq!(len1, len2);
let (tx, rx) = mpsc::channel::<MDataSeqValue>();
extern "C" fn get_cb(
user_data: *mut c_void,
res: *const FfiResult,
ptr: *const u8,
len: usize,
version: u64,
) {
unsafe {
assert_eq!((*res).error_code, 0);
let value = vec_clone_from_raw_parts(ptr, len);
let value = MDataSeqValue {
data: value,
version,
};
send_via_user_data(user_data, value)
}
}
let mut ud = Default::default();
unsafe {
seq_mdata_entries_get(
&app,
handle0,
key0.as_ptr(),
key0.len(),
sender_as_user_data(&tx, &mut ud),
get_cb,
);
};
let value = unwrap!(rx.recv());
assert_eq!(value, value0);
unsafe {
seq_mdata_entries_get(
&app,
handle0,
key1.as_ptr(),
key1.len(),
sender_as_user_data(&tx, &mut ud),
get_cb,
);
};
let value = unwrap!(rx.recv());
assert_eq!(value, value1);
let entries: Vec<MDataEntry> = unsafe {
unwrap!(call_vec(|ud, cb| seq_mdata_list_entries(
&app, handle0, ud, cb
)))
};
assert_eq!(entries.len(), 2);
assert!(entries.contains(&MDataEntry {
key: MDataKey(key0),
value: MDataValue::from_routing(value0),
}));
assert!(entries.contains(&MDataEntry {
key: MDataKey(key1),
value: MDataValue::from_routing(value1),
}));
unsafe {
unwrap!(call_0(|ud, cb| seq_mdata_entries_free(
&app, handle0, ud, cb
)));
unwrap!(call_0(|ud, cb| seq_mdata_entries_free(
&app, handle1, ud, cb
)))
}
}
#[test]
fn keys_and_values() {
let app = create_app();
let key0 = b"key0".to_vec();
let key1 = b"key1".to_vec();
let value0 = MDataValue {
content: unwrap!(utils::generate_random_vector(10)),
entry_version: 0,
};
let value1 = MDataValue {
content: unwrap!(utils::generate_random_vector(10)),
entry_version: 0,
};
let perms_set = MDataPermissionSet::new()
.allow(MDataAction::Read)
.allow(MDataAction::Insert);
let perms_h: MDataPermissionsHandle =
unsafe { unwrap!(call_1(|ud, cb| mdata_permissions_new(&app, ud, cb))) };
let app_pk_handle = unwrap!(run(&app, move |client, context| {
Ok(context
.object_cache()
.insert_pub_sign_key(client.public_key()))
}));
unsafe {
unwrap!(call_0(|ud, cb| mdata_permissions_insert(
&app,
perms_h,
app_pk_handle,
&permission_set_into_repr_c(perms_set),
ud,
cb,
)))
};
let md_info: NativeMDataInfo = unsafe {
unwrap!(call_1(|ud, cb| mdata_info_random_public(
true, 10_000, ud, cb
)))
};
let md_info = md_info.into_repr_c();
unsafe {
unwrap!(call_0(|ud, cb| mdata_put(
&app,
&md_info,
perms_h,
ENTRIES_EMPTY,
ud,
cb
)))
};
let keys: Vec<NativeMDataKey> =
unsafe { unwrap!(call_vec(|ud, cb| mdata_list_keys(&app, &md_info, ud, cb))) };
assert_eq!(keys.len(), 0);
let values: Vec<MDataValue> = unsafe {
unwrap!(call_vec(|ud, cb| seq_mdata_list_values(
&app, &md_info, ud, cb
)))
};
assert_eq!(values.len(), 0);
let actions_h: MDataEntryActionsHandle =
unsafe { unwrap!(call_1(|ud, cb| mdata_entry_actions_new(&app, ud, cb))) };
{
unsafe {
unwrap!(call_0(|ud, cb| mdata_entry_actions_insert(
&app,
actions_h,
key0.as_ptr(),
key0.len(),
value0.content.as_ptr(),
value0.content.len(),
ud,
cb,
)))
};
unsafe {
unwrap!(call_0(|ud, cb| mdata_entry_actions_insert(
&app,
actions_h,
key1.as_ptr(),
key1.len(),
value1.content.as_ptr(),
value1.content.len(),
ud,
cb,
)))
};
}
unsafe {
unwrap!(call_0(|ud, cb| mdata_mutate_entries(
&app, &md_info, actions_h, ud, cb
)))
}
let keys: Vec<MDataKey> =
unsafe { unwrap!(call_vec(|ud, cb| mdata_list_keys(&app, &md_info, ud, cb))) };
assert_eq!(keys.len(), 2);
assert!(keys.contains(&MDataKey(key0)));
assert!(keys.contains(&MDataKey(key1)));
let values: Vec<MDataValue> = unsafe {
unwrap!(call_vec(|ud, cb| seq_mdata_list_values(
&app, &md_info, ud, cb
)))
};
assert_eq!(values.len(), 2);
assert!(values.contains(&value0));
assert!(values.contains(&value1));
}
}