extern crate libc;
extern crate time;
use api::ErrorCode;
use utils::cstring::CStringUtils;
use utils::json::{JsonDecodable, JsonEncodable};
use utils::sequence::SequenceUtils;
use self::libc::c_char;
use self::time::Timespec;
use std::collections::HashMap;
use std::ffi::CString;
use std::sync::Mutex;
use std::ops::Sub;
#[derive(Deserialize)]
struct InmemWalletRuntimeConfig {
freshness_time: i64
}
impl<'a> JsonDecodable<'a> for InmemWalletRuntimeConfig {}
impl Default for InmemWalletRuntimeConfig {
fn default() -> Self {
InmemWalletRuntimeConfig { freshness_time: 1000 }
}
}
#[derive(Debug)]
struct InmemWalletContext {
name: String,
freshness_time: i64
}
#[derive(Debug)]
struct InmemWalletRecord {
key: String,
value: String,
time_created: Timespec
}
#[derive(Debug, Serialize)]
pub struct InmemWalletJSONValue {
pub key: String,
pub value: String
}
#[derive(Debug, Serialize)]
pub struct InmemWalletJSONValues {
pub values: Vec<InmemWalletJSONValue>
}
impl JsonEncodable for InmemWalletJSONValues {}
lazy_static! {
static ref INMEM_WALLETS: Mutex<HashMap<String, HashMap<String, InmemWalletRecord>>> = Default::default();
}
lazy_static! {
static ref INMEM_WALLET_HANDLES: Mutex<HashMap<i32, InmemWalletContext>> = Default::default();
}
pub struct InmemWallet {}
impl InmemWallet {
pub extern "C" fn create(name: *const c_char,
_: *const c_char,
_: *const c_char) -> ErrorCode {
check_useful_c_str!(name, ErrorCode::CommonInvalidStructure);
let mut wallets = INMEM_WALLETS.lock().unwrap();
if wallets.contains_key(&name) {
return ErrorCode::CommonInvalidState;
}
wallets.insert(name.clone(), HashMap::new());
ErrorCode::Success
}
pub extern "C" fn open(name: *const c_char,
_: *const c_char,
runtime_config: *const c_char,
_: *const c_char,
handle: *mut i32) -> ErrorCode {
check_useful_c_str!(name, ErrorCode::CommonInvalidStructure);
check_useful_opt_c_str!(runtime_config, ErrorCode::CommonInvalidStructure);
let runtime_config = match runtime_config {
Some(config) => InmemWalletRuntimeConfig::from_json(config.as_str()).unwrap(), None => InmemWalletRuntimeConfig::default()
};
let wallets = INMEM_WALLETS.lock().unwrap();
if !wallets.contains_key(&name) {
return ErrorCode::CommonInvalidState;
}
let mut handles = INMEM_WALLET_HANDLES.lock().unwrap();
let xhandle = SequenceUtils::get_next_id();
handles.insert(xhandle, InmemWalletContext {
name: name,
freshness_time: runtime_config.freshness_time
});
unsafe { *handle = xhandle };
ErrorCode::Success
}
pub extern "C" fn set(xhandle: i32,
key: *const c_char,
value: *const c_char) -> ErrorCode {
check_useful_c_str!(key, ErrorCode::CommonInvalidStructure);
check_useful_c_str!(value, ErrorCode::CommonInvalidStructure);
let handles = INMEM_WALLET_HANDLES.lock().unwrap();
if !handles.contains_key(&xhandle) {
return ErrorCode::CommonInvalidState;
}
let wallet_context = handles.get(&xhandle).unwrap();
let mut wallets = INMEM_WALLETS.lock().unwrap();
if !wallets.contains_key(&wallet_context.name) {
return ErrorCode::CommonInvalidState;
}
let wallet = wallets.get_mut(&wallet_context.name).unwrap();
wallet.insert(key.clone(), InmemWalletRecord {
key: key,
value: value,
time_created: time::get_time()
});
ErrorCode::Success
}
pub extern "C" fn get(xhandle: i32,
key: *const c_char,
value_ptr: *mut *const c_char) -> ErrorCode {
check_useful_c_str!(key, ErrorCode::CommonInvalidStructure);
let handles = INMEM_WALLET_HANDLES.lock().unwrap();
if !handles.contains_key(&xhandle) {
return ErrorCode::CommonInvalidState;
}
let wallet_context = handles.get(&xhandle).unwrap();
let wallets = INMEM_WALLETS.lock().unwrap();
if !wallets.contains_key(&wallet_context.name) {
return ErrorCode::CommonInvalidState;
}
let wallet = wallets.get(&wallet_context.name).unwrap();
if !wallet.contains_key(&key) {
return ErrorCode::WalletNotFoundError;
}
let ref value = wallet.get(&key).unwrap().value;
unsafe { *value_ptr = CString::new(value.as_str()).unwrap().into_raw(); }
ErrorCode::Success
}
pub extern "C" fn get_not_expied(xhandle: i32,
key: *const c_char,
value_ptr: *mut *const c_char) -> ErrorCode {
check_useful_c_str!(key, ErrorCode::CommonInvalidStructure);
let handles = INMEM_WALLET_HANDLES.lock().unwrap();
if !handles.contains_key(&xhandle) {
return ErrorCode::CommonInvalidState;
}
let wallet_context = handles.get(&xhandle).unwrap();
let wallets = INMEM_WALLETS.lock().unwrap();
if !wallets.contains_key(&wallet_context.name) {
return ErrorCode::CommonInvalidState;
}
let wallet = wallets.get(&wallet_context.name).unwrap();
if !wallet.contains_key(&key) {
return ErrorCode::WalletNotFoundError;
}
let ref record = wallet.get(&key).unwrap();
if time::get_time().sub(record.time_created).num_seconds() > wallet_context.freshness_time {
return ErrorCode::WalletNotFoundError;
}
unsafe { *value_ptr = CString::new(record.value.as_str()).unwrap().into_raw(); }
ErrorCode::Success
}
pub extern "C" fn list(xhandle: i32,
key_prefix: *const c_char,
values_json_ptr: *mut *const c_char) -> ErrorCode {
check_useful_c_str!(key_prefix, ErrorCode::CommonInvalidStructure);
let handles = INMEM_WALLET_HANDLES.lock().unwrap();
if !handles.contains_key(&xhandle) {
return ErrorCode::CommonInvalidState;
}
let wallet_context = handles.get(&xhandle).unwrap();
let wallets = INMEM_WALLETS.lock().unwrap();
if !wallets.contains_key(&wallet_context.name) {
return ErrorCode::CommonInvalidState;
}
let wallet = wallets.get(&wallet_context.name).unwrap();
let values = InmemWalletJSONValues {
values: wallet
.keys()
.filter(|&ref key| key.starts_with(&key_prefix))
.map(|&ref key| InmemWalletJSONValue {
key: key.clone(),
value: wallet.get(key).unwrap().value.clone()
})
.collect()
}
.to_json()
.unwrap();
unsafe { *values_json_ptr = CString::new(values.as_str()).unwrap().into_raw(); }
ErrorCode::Success
}
pub extern "C" fn close(xhandle: i32) -> ErrorCode {
let mut handles = INMEM_WALLET_HANDLES.lock().unwrap();
if !handles.contains_key(&xhandle) {
return ErrorCode::CommonInvalidState;
}
handles.remove(&xhandle);
ErrorCode::Success
}
pub extern "C" fn delete(name: *const c_char,
_: *const c_char,
_: *const c_char) -> ErrorCode {
check_useful_c_str!(name, ErrorCode::CommonInvalidStructure);
let mut wallets = INMEM_WALLETS.lock().unwrap();
if !wallets.contains_key(&name) {
return ErrorCode::CommonInvalidState;
}
wallets.remove(&name);
ErrorCode::Success
}
pub extern "C" fn free(_: i32,
value: *const c_char) -> ErrorCode {
unsafe { CString::from_raw(value as *mut c_char); }
ErrorCode::Success
}
pub fn cleanup() {
let mut wallets = INMEM_WALLETS.lock().unwrap();
wallets.clear();
let mut handles = INMEM_WALLET_HANDLES.lock().unwrap();
handles.clear();
}
}