use std::ffi::c_void;
use std::sync::Arc;
use drasi_lib::StateStoreProvider;
use drasi_plugin_sdk::ffi::{FfiGetResult, FfiResult, FfiStr, FfiStringArray, StateStoreVtable};
fn ffi_guard<T, F: FnOnce() -> T>(default: T, f: F) -> T {
match std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)) {
Ok(v) => v,
Err(_) => default,
}
}
pub struct StateStoreVtableBuilder;
impl StateStoreVtableBuilder {
pub fn build(provider: Arc<dyn StateStoreProvider>) -> StateStoreVtable {
let boxed = Box::new(provider);
let state = Box::into_raw(boxed) as *mut c_void;
StateStoreVtable {
state,
get_fn: ss_get,
set_fn: ss_set,
delete_fn: ss_delete,
contains_key_fn: ss_contains_key,
get_many_fn: ss_get_many,
set_many_fn: ss_set_many,
delete_many_fn: ss_delete_many,
clear_store_fn: ss_clear_store,
list_keys_fn: ss_list_keys,
store_exists_fn: ss_store_exists,
key_count_fn: ss_key_count,
sync_fn: ss_sync,
drop_fn: ss_drop,
}
}
}
fn provider_ref(state: *mut c_void) -> &'static dyn StateStoreProvider {
let arc = unsafe { &*(state as *const Arc<dyn StateStoreProvider>) };
arc.as_ref()
}
fn block_on<F: std::future::Future>(f: F) -> Option<F::Output> {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.ok()?;
Some(rt.block_on(f))
}
extern "C" fn ss_get(state: *mut c_void, store_id: FfiStr, key: FfiStr) -> FfiGetResult {
ffi_guard(FfiGetResult::not_found(), || {
let provider = provider_ref(state);
let store_id = unsafe { store_id.to_string() };
let key = unsafe { key.to_string() };
match block_on(provider.get(&store_id, &key)) {
Some(Ok(Some(value))) => FfiGetResult::found(value),
_ => FfiGetResult::not_found(),
}
})
}
extern "C" fn ss_set(
state: *mut c_void,
store_id: FfiStr,
key: FfiStr,
value: *const u8,
value_len: usize,
) -> FfiResult {
ffi_guard(FfiResult::err("ss_set: panic".to_string()), || {
let provider = provider_ref(state);
let store_id = unsafe { store_id.to_string() };
let key = unsafe { key.to_string() };
let value = unsafe { std::slice::from_raw_parts(value, value_len) }.to_vec();
match block_on(provider.set(&store_id, &key, value)) {
Some(Ok(())) => FfiResult::ok(),
Some(Err(e)) => FfiResult::err(e.to_string()),
None => FfiResult::err("failed to build runtime".to_string()),
}
})
}
extern "C" fn ss_delete(state: *mut c_void, store_id: FfiStr, key: FfiStr) -> FfiResult {
ffi_guard(FfiResult::err("ss_delete: panic".to_string()), || {
let provider = provider_ref(state);
let store_id = unsafe { store_id.to_string() };
let key = unsafe { key.to_string() };
match block_on(provider.delete(&store_id, &key)) {
Some(Ok(_)) => FfiResult::ok(),
Some(Err(e)) => FfiResult::err(e.to_string()),
None => FfiResult::err("failed to build runtime".to_string()),
}
})
}
extern "C" fn ss_contains_key(state: *mut c_void, store_id: FfiStr, key: FfiStr) -> FfiResult {
ffi_guard(FfiResult::err("ss_contains_key: panic".to_string()), || {
let provider = provider_ref(state);
let store_id = unsafe { store_id.to_string() };
let key = unsafe { key.to_string() };
match block_on(provider.contains_key(&store_id, &key)) {
Some(Ok(true)) => FfiResult::ok(),
Some(Ok(false)) => FfiResult::err("not_found".to_string()),
Some(Err(e)) => FfiResult::err(e.to_string()),
None => FfiResult::err("failed to build runtime".to_string()),
}
})
}
extern "C" fn ss_get_many(
state: *mut c_void,
store_id: FfiStr,
keys: *const FfiStr,
keys_count: usize,
out_values: *mut FfiGetResult,
) -> FfiResult {
ffi_guard(FfiResult::err("ss_get_many: panic".to_string()), || {
let provider = provider_ref(state);
let store_id = unsafe { store_id.to_string() };
let key_strs: Vec<String> = (0..keys_count)
.map(|i| unsafe { (*keys.add(i)).to_string() })
.collect();
let key_refs: Vec<&str> = key_strs.iter().map(|s| s.as_str()).collect();
match block_on(provider.get_many(&store_id, &key_refs)) {
Some(Ok(results)) => {
for (i, key) in key_strs.iter().enumerate() {
let ffi_result = match results.get(key) {
Some(value) => FfiGetResult::found(value.clone()),
None => FfiGetResult::not_found(),
};
unsafe { *out_values.add(i) = ffi_result };
}
FfiResult::ok()
}
Some(Err(e)) => FfiResult::err(e.to_string()),
None => FfiResult::err("failed to build runtime".to_string()),
}
})
}
extern "C" fn ss_set_many(
state: *mut c_void,
store_id: FfiStr,
keys: *const FfiStr,
values: *const *const u8,
value_lens: *const usize,
count: usize,
) -> FfiResult {
ffi_guard(FfiResult::err("ss_set_many: panic".to_string()), || {
let provider = provider_ref(state);
let store_id = unsafe { store_id.to_string() };
let entries: Vec<(String, Vec<u8>)> = (0..count)
.map(|i| unsafe {
let key = (*keys.add(i)).to_string();
let len = *value_lens.add(i);
let val = std::slice::from_raw_parts(*values.add(i), len).to_vec();
(key, val)
})
.collect();
let refs: Vec<(&str, &[u8])> = entries
.iter()
.map(|(k, v)| (k.as_str(), v.as_slice()))
.collect();
match block_on(provider.set_many(&store_id, &refs)) {
Some(Ok(())) => FfiResult::ok(),
Some(Err(e)) => FfiResult::err(e.to_string()),
None => FfiResult::err("failed to build runtime".to_string()),
}
})
}
extern "C" fn ss_delete_many(
state: *mut c_void,
store_id: FfiStr,
keys: *const FfiStr,
keys_count: usize,
) -> i64 {
ffi_guard(-1, || {
let provider = provider_ref(state);
let store_id = unsafe { store_id.to_string() };
let key_strs: Vec<String> = (0..keys_count)
.map(|i| unsafe { (*keys.add(i)).to_string() })
.collect();
let key_refs: Vec<&str> = key_strs.iter().map(|s| s.as_str()).collect();
match block_on(provider.delete_many(&store_id, &key_refs)) {
Some(Ok(count)) => count as i64,
_ => -1,
}
})
}
extern "C" fn ss_clear_store(state: *mut c_void, store_id: FfiStr) -> i64 {
ffi_guard(-1, || {
let provider = provider_ref(state);
let store_id = unsafe { store_id.to_string() };
match block_on(provider.clear_store(&store_id)) {
Some(Ok(count)) => count as i64,
_ => -1,
}
})
}
extern "C" fn ss_list_keys(state: *mut c_void, store_id: FfiStr) -> FfiStringArray {
ffi_guard(FfiStringArray::from_vec(Vec::new()), || {
let provider = provider_ref(state);
let store_id = unsafe { store_id.to_string() };
match block_on(provider.list_keys(&store_id)) {
Some(Ok(keys)) => FfiStringArray::from_vec(keys),
_ => FfiStringArray::from_vec(Vec::new()),
}
})
}
extern "C" fn ss_store_exists(state: *mut c_void, store_id: FfiStr) -> FfiResult {
ffi_guard(FfiResult::err("ss_store_exists: panic".to_string()), || {
let provider = provider_ref(state);
let store_id = unsafe { store_id.to_string() };
match block_on(provider.store_exists(&store_id)) {
Some(Ok(true)) => FfiResult::ok(),
Some(Ok(false)) => FfiResult::err("not_found".to_string()),
Some(Err(e)) => FfiResult::err(e.to_string()),
None => FfiResult::err("failed to build runtime".to_string()),
}
})
}
extern "C" fn ss_key_count(state: *mut c_void, store_id: FfiStr) -> i64 {
ffi_guard(-1, || {
let provider = provider_ref(state);
let store_id = unsafe { store_id.to_string() };
match block_on(provider.key_count(&store_id)) {
Some(Ok(count)) => count as i64,
_ => -1,
}
})
}
extern "C" fn ss_sync(state: *mut c_void) -> FfiResult {
ffi_guard(FfiResult::err("ss_sync: panic".to_string()), || {
let provider = provider_ref(state);
match block_on(provider.sync()) {
Some(Ok(())) => FfiResult::ok(),
Some(Err(e)) => FfiResult::err(e.to_string()),
None => FfiResult::err("failed to build runtime".to_string()),
}
})
}
extern "C" fn ss_drop(state: *mut c_void) {
ffi_guard((), || {
unsafe { drop(Box::from_raw(state as *mut Arc<dyn StateStoreProvider>)) };
})
}